diff --git a/.gitignore b/.gitignore index f0a8a647..9eb4afab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,39 +1,60 @@ - +# General .DS_Store -*.asv -*.m~ -*.mat + *.xml *.log -*.html *.zip +*.html *.ps +*.png +# Project specific options_task-*date*.json onsets*_events.mat -# files in the demo folder related to running the demo analysis -demos/*/outputs/ -demos/*/inputs/ -demos/*/*.nii -demos/*/cfg/*.json - -# test folder and dummy data -tests/*.png -tests/group/* -tests/*/*.json -tests/dummyData/derivatives/cpp_spm*/sub-*/ - -# ignore content of the build folder of the doc -docs/build/* - -# ignore virtual env +## virtual env cpp_spm/* env/* +venv/* -# visual studio code stuff +## visual studio code .vscode +## MATLAB / OCTAVE gitignore template + +# From : https://github.com/github/gitignore/blob/master/Global/MATLAB.gitignore + + +# Windows default autosave extension +*.asv + +# OSX / *nix default autosave extension +*.m~ + +# Compiled MEX binaries (all platforms) +*.mex* + +# Packaged app and toolbox files +*.mlappinstall +*.mltbx + +# Generated helpsearch folders +helpsearch*/ + +# Simulink code generation folders +slprj/ +sccprj/ + +# Matlab code generation folders +codegen/ + +# Simulink autosave extension +*.autosave + +# Simulink cache files +*.slxc +# Octave session info +octave-workspace diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..a2858fb6 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,28 @@ +repos: + +- repo: local + + hooks: + + - id: mh_style + name: mh_style + entry: mh_style + args: [--process-slx, --fix] + files: ^(.*\.(m|slx))$ + language: python + additional_dependencies: [miss_hit_core] + + - id: mh_metric + name: mh_metric + entry: mh_metric + args: [--ci] + files: ^(.*\.(m|slx))$ + language: python + additional_dependencies: [miss_hit_core] + + - id: mh_lint + name: mh_lint + entry: mh_lint + files: ^(.*\.(m|slx))$ + language: python + additional_dependencies: [miss_hit] \ No newline at end of file diff --git a/README.md b/README.md index 681cf30e..80967101 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,15 @@ **Documentation** -[![Documentation Status: stable](https://readthedocs.org/projects/cpp-bids-spm/badge/?version=stable)](https://cpp-bids-spm.readthedocs.io/en/stable/?badge=stable) +[![Documentation Status: stable](https://readthedocs.org/projects/cpp_spm/badge/?version=stable)](https://cpp_spm.readthedocs.io/en/stable/?badge=stable) **Code quality and style** -[![](https://img.shields.io/badge/Octave-CI-blue?logo=Octave&logoColor=white)](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/actions) -![](https://github.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline/workflows/CI/badge.svg) +[![](https://img.shields.io/badge/Octave-CI-blue?logo=Octave&logoColor=white)](https://github.com/cpp-lln-lab/CPP_SPM/actions) +![](https://github.com/cpp-lln-lab/CPP_SPM/workflows/CI/badge.svg) **Unit tests and coverage** - -[![Build Status](https://travis-ci.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline.svg?branch=master)](https://travis-ci.com/cpp-lln-lab/CPP_BIDS_SPM_pipeline) -[![codecov](https://codecov.io/gh/Remi-Gau/CPP_SPM/branch/master/graph/badge.svg?token=8IoRQtbFUV)](https://codecov.io/gh/Remi-Gau/CPP_SPM) +[![codecov](https://codecov.io/gh/cpp-lln-lab/CPP_SPM/branch/master/graph/badge.svg?token=8IoRQtbFUV)](https://codecov.io/gh/cpp-lln-lab/CPP_SPM) **How to cite** @@ -62,7 +60,7 @@ initCppSpm ``` Please see our -[documentation](https://cpp-bids-spm.readthedocs.io/en/latest/index.html) for +[documentation](https://cpp_spm.readthedocs.io/en/latest/index.html) for more detailed instructions. ### Dependencies @@ -111,7 +109,7 @@ This can perform: The core functions are in the `src` folder. Please see our -[documentation](https://cpp-bids-spm.readthedocs.io/en/latest/index.html) for +[documentation](https://cpp_spm.readthedocs.io/en/latest/index.html) for more info. ## Octave compatibility @@ -145,6 +143,23 @@ Scripts names in general and as well functions related to the demos use a Constant are written in `UPPERCASE`. +#### Pre-commit + +There is a [pre-commit hook](https://pre-commit.com/) that you can use to +reformat files as you commit them. + +Install pre-commit by using our `requirements.txt` file +```bash +pip install -r requirements.txt +``` + +Install the hook +```bash +pre-commit install +``` + +You're done. `mh_style --fix` will now be run every time you commit. + ## Contributors Thanks goes to these wonderful people diff --git a/demos/.gitignore b/demos/.gitignore new file mode 100644 index 00000000..4458c13e --- /dev/null +++ b/demos/.gitignore @@ -0,0 +1,5 @@ +# files in the demo folder related to running the demo analysis +*/outputs/ +*/inputs/ +*/*.nii +*/cfg/*.json \ No newline at end of file diff --git a/demos/MoAE/moae_run.m b/demos/MoAE/moae_run.m index 4d4d71b0..ac2c4c9c 100644 --- a/demos/MoAE/moae_run.m +++ b/demos/MoAE/moae_run.m @@ -27,26 +27,29 @@ bidsCopyRawFolder(opt, 1); % In case you just want to run segmentation and skull stripping -% bidsSegmentSkullStrip(opt); -% % NOTE: skull stripping is also included in 'bidsSpatialPrepro' +bidsSegmentSkullStrip(opt); bidsSTC(opt); bidsSpatialPrepro(opt); % The following do not run on octave for now (because of spmup) -anatomicalQA(opt); -bidsResliceTpmToFunc(opt); -functionalQA(opt); - +% anatomicalQA(opt); +% bidsResliceTpmToFunc(opt); +% functionalQA(opt); + +% create a whole brain functional mean image mask +% so the mask will be in the same resolution/space as the functional images +% one may not need it if they are running bidsFFX +% since it creates a mask.nii by default +opt.skullstrip.mean = 1; +mask = bidsWholeBrainFuncMask(opt); + +% smoooth the funcitional images bidsSmoothing(FWHM, opt); -% The following crash on CI -WD = pwd; +% The following crash on Travis CI bidsFFX('specifyAndEstimate', opt, FWHM); -cd(WD); bidsFFX('contrasts', opt, FWHM); -cd(WD); bidsResults(opt, FWHM); -cd(WD); diff --git a/demos/lesion_detection/batch_lesion.m b/demos/lesion_detection/batch_lesion.m deleted file mode 100644 index 87de2b2b..00000000 --- a/demos/lesion_detection/batch_lesion.m +++ /dev/null @@ -1,20 +0,0 @@ -% (C) Copyright 2021 CPP_SPM developers - -clear; -clc; - -% URL of the data set to download -% URL = https://gin.g-node.org/mwmaclean/CVI-Datalad/src/master/data - -run ../../initCppSpm.m; - -%% Get Data -opt = Lesion_getOption(); - -%% Run batches -reportBIDS(opt); - -deleteZippedNii = true; -bidsCopyRawFolder(opt, deleteZippedNii, {'anat'}); - -bidsLesionSegmentation(opt); diff --git a/demos/lesion_detection/lesion_get_option.m b/demos/lesion_detection/lesion_get_option.m old mode 100644 new mode 100755 index 2da9718f..a9608e1b --- a/demos/lesion_detection/lesion_get_option.m +++ b/demos/lesion_detection/lesion_get_option.m @@ -5,14 +5,15 @@ % % USAGE:: % - % opt = Lesion_getOption() + % opt = lesion_get_option() % - % :returns: - :optSource: (struct) + % :returns: - :opt: (struct) % % (C) Copyright 2021 CPP_SPM developers % The directory where the data are located - opt.dataDir = '/home/remi/gin/CVI-Datalad/data'; + opt.dataDir = 'C:\Users\michm\Data\myphdproject\MRI\CVI-DataLad\data'; + opt.derivativesDir = fullfile(opt.dataDir, '..', 'outputs'); %% DO NOT TOUCH opt = checkOptions(opt); diff --git a/demos/lesion_detection/run_lesion.m b/demos/lesion_detection/run_lesion.m new file mode 100755 index 00000000..79c18e00 --- /dev/null +++ b/demos/lesion_detection/run_lesion.m @@ -0,0 +1,32 @@ +% (C) Copyright 2021 CPP_SPM developers + +clear; +clc; + +% This script will will run +% 1) lesion segmentation, +% 2) lesion abnormalities detection and +% 3) lesion overlapmap adapted from the ALI toolbox (SPM.) + +% URL of the data set to download +% URL = https://gin.g-node.org/mwmaclean/CVI-Datalad/src/master/data + +run ../../initCppSpm.m; + +%% Set options +opt = lesion_get_option(); + +%% Run batches +reportBIDS(opt); + +deleteZippedNii = true; +bidsCopyRawFolder(opt, deleteZippedNii, {'anat'}); + +% Step 1: segmentation +bidsLesionSegmentation(opt); + +% % Step 2: lesion abnormalities +bidsLesionAbnormalitiesDetection(opt); + +% % Step 3: overlap map +% bidsLesionOverlapMap(opt) diff --git a/demos/openneuro/ds000114_get_option.m b/demos/openneuro/ds000114_get_option.m index 2999d75a..24a873a9 100644 --- a/demos/openneuro/ds000114_get_option.m +++ b/demos/openneuro/ds000114_get_option.m @@ -6,10 +6,6 @@ % % (C) Copyright 2020 CPP_SPM developers - if nargin < 1 - opt = []; - end - % The directory where the data are located opt.dataDir = '/home/remi/openneuro/ds000114/raw'; @@ -21,7 +17,6 @@ opt.anatReference.type = 'T1w'; opt.anatReference.session = 'retest'; - % Uncomment the lines below to run preprocessing % - don't use realign and unwarp % opt.realign.useUnwarp = false; @@ -30,11 +25,11 @@ opt.model.file = fullfile(fileparts(mfilename('fullpath')), ... 'models', ... - 'model-ds000114-linebisection_smdl.json'); + 'model-ds000114-lineBisectionRunLevel_smdl.json'); % specify the result to compute opt.result.Steps(1) = struct( ... - 'Level', 'subject', ... + 'Level', 'run', ... 'Contrasts', struct( ... 'Name', 'Correct_Task', ... % has to match 'Mask', false, ... diff --git a/demos/openneuro/ds000114_run.m b/demos/openneuro/ds000114_run.m index f3d8dd1d..07a7a7cc 100644 --- a/demos/openneuro/ds000114_run.m +++ b/demos/openneuro/ds000114_run.m @@ -30,8 +30,31 @@ bidsSmoothing(FWHM, opt); +%% Run level analysis: as for MVPA + bidsFFX('specifyAndEstimate', opt, FWHM); bidsFFX('contrasts', opt, FWHM); + +bidsConcatBetaTmaps(opt, FWHM, false, false); + +%% Subject level analysis: for regular univariate + +opt.model.file = fullfile(fileparts(mfilename('fullpath')), ... + 'models', ... + 'model-ds000114-linebisection_smdl.json'); + +bidsFFX('specifyAndEstimate', opt, FWHM); + +opt.result.Steps(1) = struct( ... + 'Level', 'subject', ... + 'Contrasts', struct( ... + 'Name', 'Correct_Task', ... % has to match + 'Mask', false, ... + 'MC', 'FWE', ... FWE, none, FDR + 'p', 0.05, ... + 'k', 0)); + +bidsFFX('contrasts', opt, FWHM); bidsResults(opt, FWHM); bidsRFX('smoothContrasts', opt, FWHM, conFWHM); diff --git a/demos/openneuro/models/model-ds000114-lineBisectionRunLevel_smdl.json b/demos/openneuro/models/model-ds000114-lineBisectionRunLevel_smdl.json new file mode 100644 index 00000000..c58c3c27 --- /dev/null +++ b/demos/openneuro/models/model-ds000114-lineBisectionRunLevel_smdl.json @@ -0,0 +1,31 @@ +{ + "Name": "run level", + "Description": "contrasts to compute for the line bisection task", + "Input": { + "task": "linebisection" + }, + "Steps": [ + { + "Level": "run", + "Model": { + "X": [ + "trial_type.Correct_Task", + "trial_type.Incorrect_Task", + "trial_type.No_Response_Task", + "trial_type.Response_Control", + "trial_type.No_Response_Control", + "trans_x", + "trans_y", + "trans_z", + "rot_x", + "rot_y", + "rot_z" + ] + }, + "AutoContrasts": [ + "trial_type.Correct_Task", + "trial_type.Incorrect_Task" + ] + } + ] +} \ No newline at end of file diff --git a/demos/openneuro/models/model-ds000114-linebisection_smdl.json b/demos/openneuro/models/model-ds000114-linebisection_smdl.json index 7bd81d2c..dae89ed5 100644 --- a/demos/openneuro/models/model-ds000114-linebisection_smdl.json +++ b/demos/openneuro/models/model-ds000114-linebisection_smdl.json @@ -14,10 +14,18 @@ "trial_type.No_Response_Task", "trial_type.Response_Control", "trial_type.No_Response_Control", - "trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z" + "trans_x", + "trans_y", + "trans_z", + "rot_x", + "rot_y", + "rot_z" ] }, - "AutoContrasts": ["trial_type.Correct_Task", "trial_type.Incorrect_Task"] + "AutoContrasts": [ + "trial_type.Correct_Task", + "trial_type.Incorrect_Task" + ] }, { "Level": "run", @@ -28,14 +36,25 @@ "trial_type.No_Response_Task", "trial_type.Response_Control", "trial_type.No_Response_Control", - "trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z" + "trans_x", + "trans_y", + "trans_z", + "rot_x", + "rot_y", + "rot_z" ] }, - "AutoContrasts": ["trial_type.Correct_Task", "trial_type.Incorrect_Task"] + "AutoContrasts": [ + "trial_type.Correct_Task", + "trial_type.Incorrect_Task" + ] }, { "Level": "dataset", - "AutoContrasts": ["trial_type.Correct_Task", "trial_type.Incorrect_Task"] + "AutoContrasts": [ + "trial_type.Correct_Task", + "trial_type.Incorrect_Task" + ] } ] } \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..6dd6610c --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +# documentation +build/* \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile index d0c3cbf1..3eaac87d 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -18,3 +18,6 @@ help: # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +doc: + @$(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)" diff --git a/lib/NiftiTools/affine.m b/lib/NiftiTools/affine.m index 13b652f3..81e91cba 100644 --- a/lib/NiftiTools/affine.m +++ b/lib/NiftiTools/affine.m @@ -1,6 +1,6 @@ % Using 2D or 3D affine matrix to rotate, translate, scale, reflect and % shear a 2D image or 3D volume. 2D image is represented by a 2D matrix, -% 3D volume is represented by a 3D matrix, and data type can be real +% 3D volume is represented by a 3D matrix, and data type can be real % integer or floating-point. % % You may notice that MATLAB has a function called 'imtransform.m' for @@ -13,542 +13,540 @@ % for 3D volume. % % Usage: [new_img new_M] = ... -% affine(old_img, old_M, [new_elem_size], [verbose], [bg], [method]); +% affine(old_img, old_M, [new_elem_size], [verbose], [bg], [method]); % -% old_img - original 2D image or 3D volume. We assume x for the 1st -% dimension, y for the 2nd dimension, and z for the 3rd -% dimension. +% old_img - original 2D image or 3D volume. We assume x for the 1st +% dimension, y for the 2nd dimension, and z for the 3rd +% dimension. % -% old_M - a 3x3 2D affine matrix for 2D image, or a 4x4 3D affine -% matrix for 3D volume. We assume x for the 1st dimension, -% y for the 2nd dimension, and z for the 3rd dimension. +% old_M - a 3x3 2D affine matrix for 2D image, or a 4x4 3D affine +% matrix for 3D volume. We assume x for the 1st dimension, +% y for the 2nd dimension, and z for the 3rd dimension. % -% new_elem_size (optional) - size of voxel along x y z direction for -% a transformed 3D volume, or size of pixel along x y for -% a transformed 2D image. We assume x for the 1st dimension -% y for the 2nd dimension, and z for the 3rd dimension. -% 'new_elem_size' is 1 if it is default or empty. +% new_elem_size (optional) - size of voxel along x y z direction for +% a transformed 3D volume, or size of pixel along x y for +% a transformed 2D image. We assume x for the 1st dimension +% y for the 2nd dimension, and z for the 3rd dimension. +% 'new_elem_size' is 1 if it is default or empty. % -% You can increase its value to decrease the resampling rate, -% and make the 2D image or 3D volume more coarse. It works -% just like 'interp3'. +% You can increase its value to decrease the resampling rate, +% and make the 2D image or 3D volume more coarse. It works +% just like 'interp3'. % % verbose (optional) - 1, 0 -% 1: show transforming progress in percentage -% 2: progress will not be displayed -% 'verbose' is 1 if it is default or empty. +% 1: show transforming progress in percentage +% 2: progress will not be displayed +% 'verbose' is 1 if it is default or empty. % -% bg (optional) - background voxel intensity in any extra corner that -% is caused by the interpolation. 0 in most cases. If it is -% default or empty, 'bg' will be the average of two corner -% voxel intensities in original data. +% bg (optional) - background voxel intensity in any extra corner that +% is caused by the interpolation. 0 in most cases. If it is +% default or empty, 'bg' will be the average of two corner +% voxel intensities in original data. % -% method (optional) - 1, 2, or 3 -% 1: for Trilinear interpolation -% 2: for Nearest Neighbor interpolation -% 3: for Fischer's Bresenham interpolation -% 'method' is 1 if it is default or empty. +% method (optional) - 1, 2, or 3 +% 1: for Trilinear interpolation +% 2: for Nearest Neighbor interpolation +% 3: for Fischer's Bresenham interpolation +% 'method' is 1 if it is default or empty. % -% new_img - transformed 2D image or 3D volume +% new_img - transformed 2D image or 3D volume % -% new_M - transformed affine matrix +% new_M - transformed affine matrix % % Example 1 (3D rotation): -% load mri.mat; old_img = double(squeeze(D)); -% old_M = [0.88 0.5 3 -90; -0.5 0.88 3 -126; 0 0 2 -72; 0 0 0 1]; -% new_img = affine(old_img, old_M, 2); -% [x y z] = meshgrid(1:128,1:128,1:27); -% sz = size(new_img); -% [x1 y1 z1] = meshgrid(1:sz(2),1:sz(1),1:sz(3)); -% figure; slice(x, y, z, old_img, 64, 64, 13.5); -% shading flat; colormap(map); view(-66, 66); -% figure; slice(x1, y1, z1, new_img, sz(1)/2, sz(2)/2, sz(3)/2); -% shading flat; colormap(map); view(-66, 66); +% load mri.mat; old_img = double(squeeze(D)); +% old_M = [0.88 0.5 3 -90; -0.5 0.88 3 -126; 0 0 2 -72; 0 0 0 1]; +% new_img = affine(old_img, old_M, 2); +% [x y z] = meshgrid(1:128,1:128,1:27); +% sz = size(new_img); +% [x1 y1 z1] = meshgrid(1:sz(2),1:sz(1),1:sz(3)); +% figure; slice(x, y, z, old_img, 64, 64, 13.5); +% shading flat; colormap(map); view(-66, 66); +% figure; slice(x1, y1, z1, new_img, sz(1)/2, sz(2)/2, sz(3)/2); +% shading flat; colormap(map); view(-66, 66); % % Example 2 (2D interpolation): -% load mri.mat; old_img=D(:,:,1,13)'; -% old_M = [1 0 0; 0 1 0; 0 0 1]; -% new_img = affine(old_img, old_M, [.2 .4]); -% figure; image(old_img); colormap(map); -% figure; image(new_img); colormap(map); +% load mri.mat; old_img=D(:,:,1,13)'; +% old_M = [1 0 0; 0 1 0; 0 0 1]; +% new_img = affine(old_img, old_M, [.2 .4]); +% figure; image(old_img); colormap(map); +% figure; image(new_img); colormap(map); % % This program is inspired by: % SPM5 Software from Wellcome Trust Centre for Neuroimaging -% http://www.fil.ion.ucl.ac.uk/spm/software +% http://www.fil.ion.ucl.ac.uk/spm/software % Fischer, J., A. del Rio (2004). A Fast Method for Applying Rigid -% Transformations to Volume Data, WSCG2004 Conference. -% http://wscg.zcu.cz/wscg2004/Papers_2004_Short/M19.pdf -% +% Transformations to Volume Data, WSCG2004 Conference. +% http://wscg.zcu.cz/wscg2004/Papers_2004_Short/M19.pdf +% % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % function [new_img, new_M] = affine(old_img, old_M, new_elem_size, verbose, bg, method) - if ~exist('old_img','var') | ~exist('old_M','var') - error('Usage: [new_img new_M] = affine(old_img, old_M, [new_elem_size], [verbose], [bg], [method]);'); - end - - if ndims(old_img) == 3 - if ~isequal(size(old_M),[4 4]) - error('old_M should be a 4x4 affine matrix for 3D volume.'); - end - elseif ndims(old_img) == 2 - if ~isequal(size(old_M),[3 3]) - error('old_M should be a 3x3 affine matrix for 2D image.'); - end - else - error('old_img should be either 2D image or 3D volume.'); - end - - if ~exist('new_elem_size','var') | isempty(new_elem_size) - new_elem_size = [1 1 1]; - elseif length(new_elem_size) < 2 - new_elem_size = new_elem_size(1)*ones(1,3); - elseif length(new_elem_size) < 3 - new_elem_size = [new_elem_size(:); 1]'; - end - - if ~exist('method','var') | isempty(method) - method = 1; - elseif ~exist('bresenham_line3d.m','file') & method == 3 - error([char(10) char(10) 'Please download 3D Bresenham''s line generation program from:' char(10) char(10) 'http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=21057' char(10) char(10) 'to test Fischer''s Bresenham interpolation method.' char(10) char(10)]); - end - - % Make compatible to MATLAB earlier than version 7 (R14), which - % can only perform arithmetic on double data type - % - old_img = double(old_img); - old_dim = size(old_img); - - if ~exist('bg','var') | isempty(bg) - bg = mean([old_img(1) old_img(end)]); - end - - if ~exist('verbose','var') | isempty(verbose) - verbose = 1; - end - - if ndims(old_img) == 2 - old_dim(3) = 1; - old_M = old_M(:, [1 2 3 3]); - old_M = old_M([1 2 3 3], :); - old_M(3,:) = [0 0 1 0]; - old_M(:,3) = [0 0 1 0]'; - end - - % Vertices of img in voxel - % - XYZvox = [ 1 1 1 - 1 1 old_dim(3) - 1 old_dim(2) 1 - 1 old_dim(2) old_dim(3) - old_dim(1) 1 1 - old_dim(1) 1 old_dim(3) - old_dim(1) old_dim(2) 1 - old_dim(1) old_dim(2) old_dim(3) ]'; - - old_R = old_M(1:3,1:3); - old_T = old_M(1:3,4); - - % Vertices of img in millimeter - % - XYZmm = old_R*(XYZvox-1) + repmat(old_T, [1, 8]); - - % Make scale of new_M according to new_elem_size - % - new_M = diag([new_elem_size 1]); - - % Make translation so minimum vertex is moved to [1,1,1] - % - new_M(1:3,4) = round( min(XYZmm,[],2) ); - - % New dimensions will be the maximum vertices in XYZ direction (dim_vox) - % i.e. compute dim_vox via dim_mm = R*(dim_vox-1)+T - % where, dim_mm = round(max(XYZmm,[],2)); - % - new_dim = ceil(new_M(1:3,1:3) \ ( round(max(XYZmm,[],2))-new_M(1:3,4) )+1)'; - - % Initialize new_img with new_dim - % - new_img = zeros(new_dim(1:3)); - - % Mask out any changes from Z axis of transformed volume, since we - % will traverse it voxel by voxel below. We will only apply unit - % increment of mask_Z(3,4) to simulate the cursor movement - % - % i.e. we will use mask_Z * new_XYZvox to replace new_XYZvox - % - mask_Z = diag(ones(1,4)); - mask_Z(3,3) = 0; - - % It will be easier to do the interpolation if we invert the process - % by not traversing the original volume. Instead, we traverse the - % transformed volume, and backproject each voxel in the transformed - % volume back into the original volume. If the backprojected voxel - % in original volume is within its boundary, the intensity of that - % voxel can be used by the cursor location in the transformed volume. - % - % First, we traverse along Z axis of transformed volume voxel by voxel - % - for z = 1:new_dim(3) - - if verbose & ~mod(z,10) - fprintf('%.2f percent is done.\n', 100*z/new_dim(3)); - end - - % We need to find out the mapping from voxel in the transformed - % volume (new_XYZvox) to voxel in the original volume (old_XYZvox) - % - % The following equation works, because they all equal to XYZmm: - % new_R*(new_XYZvox-1) + new_T == old_R*(old_XYZvox-1) + old_T - % - % We can use modified new_M1 & old_M1 to substitute new_M & old_M - % new_M1 * new_XYZvox == old_M1 * old_XYZvox - % - % where: M1 = M; M1(:,4) = M(:,4) - sum(M(:,1:3),2); - % and: M(:,4) == [T; 1] == sum(M1,2) - % - % Therefore: old_XYZvox = old_M1 \ new_M1 * new_XYZvox; - % - % Since we are traverse Z axis, and new_XYZvox is replaced - % by mask_Z * new_XYZvox, the above formula can be rewritten - % as: old_XYZvox = old_M1 \ new_M1 * mask_Z * new_XYZvox; - % - % i.e. we find the mapping from new_XYZvox to old_XYZvox: - % M = old_M1 \ new_M1 * mask_Z; - % - % First, compute modified old_M1 & new_M1 - % - old_M1 = old_M; old_M1(:,4) = old_M(:,4) - sum(old_M(:,1:3),2); - new_M1 = new_M; new_M1(:,4) = new_M(:,4) - sum(new_M(:,1:3),2); - - % Then, apply unit increment of mask_Z(3,4) to simulate the - % cursor movement - % - mask_Z(3,4) = z; - - % Here is the mapping from new_XYZvox to old_XYZvox - % - M = old_M1 \ new_M1 * mask_Z; - - switch method + if ~exist('old_img', 'var') | ~exist('old_M', 'var') + error('Usage: [new_img new_M] = affine(old_img, old_M, [new_elem_size], [verbose], [bg], [method]);'); + end + + if ndims(old_img) == 3 + if ~isequal(size(old_M), [4 4]) + error('old_M should be a 4x4 affine matrix for 3D volume.'); + end + elseif ndims(old_img) == 2 + if ~isequal(size(old_M), [3 3]) + error('old_M should be a 3x3 affine matrix for 2D image.'); + end + else + error('old_img should be either 2D image or 3D volume.'); + end + + if ~exist('new_elem_size', 'var') | isempty(new_elem_size) + new_elem_size = [1 1 1]; + elseif length(new_elem_size) < 2 + new_elem_size = new_elem_size(1) * ones(1, 3); + elseif length(new_elem_size) < 3 + new_elem_size = [new_elem_size(:); 1]'; + end + + if ~exist('method', 'var') | isempty(method) + method = 1; + elseif ~exist('bresenham_line3d.m', 'file') & method == 3 + error([char(10) char(10) 'Please download 3D Bresenham''s line generation program from:' char(10) char(10) 'http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=21057' char(10) char(10) 'to test Fischer''s Bresenham interpolation method.' char(10) char(10)]); + end + + % Make compatible to MATLAB earlier than version 7 (R14), which + % can only perform arithmetic on double data type + % + old_img = double(old_img); + old_dim = size(old_img); + + if ~exist('bg', 'var') | isempty(bg) + bg = mean([old_img(1) old_img(end)]); + end + + if ~exist('verbose', 'var') | isempty(verbose) + verbose = 1; + end + + if ndims(old_img) == 2 + old_dim(3) = 1; + old_M = old_M(:, [1 2 3 3]); + old_M = old_M([1 2 3 3], :); + old_M(3, :) = [0 0 1 0]; + old_M(:, 3) = [0 0 1 0]'; + end + + % Vertices of img in voxel + % + XYZvox = [1 1 1 + 1 1 old_dim(3) + 1 old_dim(2) 1 + 1 old_dim(2) old_dim(3) + old_dim(1) 1 1 + old_dim(1) 1 old_dim(3) + old_dim(1) old_dim(2) 1 + old_dim(1) old_dim(2) old_dim(3)]'; + + old_R = old_M(1:3, 1:3); + old_T = old_M(1:3, 4); + + % Vertices of img in millimeter + % + XYZmm = old_R * (XYZvox - 1) + repmat(old_T, [1, 8]); + + % Make scale of new_M according to new_elem_size + % + new_M = diag([new_elem_size 1]); + + % Make translation so minimum vertex is moved to [1,1,1] + % + new_M(1:3, 4) = round(min(XYZmm, [], 2)); + + % New dimensions will be the maximum vertices in XYZ direction (dim_vox) + % i.e. compute dim_vox via dim_mm = R*(dim_vox-1)+T + % where, dim_mm = round(max(XYZmm,[],2)); + % + new_dim = ceil(new_M(1:3, 1:3) \ (round(max(XYZmm, [], 2)) - new_M(1:3, 4)) + 1)'; + + % Initialize new_img with new_dim + % + new_img = zeros(new_dim(1:3)); + + % Mask out any changes from Z axis of transformed volume, since we + % will traverse it voxel by voxel below. We will only apply unit + % increment of mask_Z(3,4) to simulate the cursor movement + % + % i.e. we will use mask_Z * new_XYZvox to replace new_XYZvox + % + mask_Z = diag(ones(1, 4)); + mask_Z(3, 3) = 0; + + % It will be easier to do the interpolation if we invert the process + % by not traversing the original volume. Instead, we traverse the + % transformed volume, and backproject each voxel in the transformed + % volume back into the original volume. If the backprojected voxel + % in original volume is within its boundary, the intensity of that + % voxel can be used by the cursor location in the transformed volume. + % + % First, we traverse along Z axis of transformed volume voxel by voxel + % + for z = 1:new_dim(3) + + if verbose & ~mod(z, 10) + fprintf('%.2f percent is done.\n', 100 * z / new_dim(3)); + end + + % We need to find out the mapping from voxel in the transformed + % volume (new_XYZvox) to voxel in the original volume (old_XYZvox) + % + % The following equation works, because they all equal to XYZmm: + % new_R*(new_XYZvox-1) + new_T == old_R*(old_XYZvox-1) + old_T + % + % We can use modified new_M1 & old_M1 to substitute new_M & old_M + % new_M1 * new_XYZvox == old_M1 * old_XYZvox + % + % where: M1 = M; M1(:,4) = M(:,4) - sum(M(:,1:3),2); + % and: M(:,4) == [T; 1] == sum(M1,2) + % + % Therefore: old_XYZvox = old_M1 \ new_M1 * new_XYZvox; + % + % Since we are traverse Z axis, and new_XYZvox is replaced + % by mask_Z * new_XYZvox, the above formula can be rewritten + % as: old_XYZvox = old_M1 \ new_M1 * mask_Z * new_XYZvox; + % + % i.e. we find the mapping from new_XYZvox to old_XYZvox: + % M = old_M1 \ new_M1 * mask_Z; + % + % First, compute modified old_M1 & new_M1 + % + old_M1 = old_M; + old_M1(:, 4) = old_M(:, 4) - sum(old_M(:, 1:3), 2); + new_M1 = new_M; + new_M1(:, 4) = new_M(:, 4) - sum(new_M(:, 1:3), 2); + + % Then, apply unit increment of mask_Z(3,4) to simulate the + % cursor movement + % + mask_Z(3, 4) = z; + + % Here is the mapping from new_XYZvox to old_XYZvox + % + M = old_M1 \ new_M1 * mask_Z; + + switch method case 1 - new_img(:,:,z) = trilinear(old_img, new_dim, old_dim, M, bg); + new_img(:, :, z) = trilinear(old_img, new_dim, old_dim, M, bg); case 2 - new_img(:,:,z) = nearest_neighbor(old_img, new_dim, old_dim, M, bg); + new_img(:, :, z) = nearest_neighbor(old_img, new_dim, old_dim, M, bg); case 3 - new_img(:,:,z) = bresenham(old_img, new_dim, old_dim, M, bg); - end + new_img(:, :, z) = bresenham(old_img, new_dim, old_dim, M, bg); + end - end; % for z + end % for z - if ndims(old_img) == 2 - new_M(3,:) = []; - new_M(:,3) = []; - end + if ndims(old_img) == 2 + new_M(3, :) = []; + new_M(:, 3) = []; + end - return; % affine + return % affine - -%-------------------------------------------------------------------- + % -------------------------------------------------------------------- function img_slice = trilinear(img, dim1, dim2, M, bg) - img_slice = zeros(dim1(1:2)); - TINY = 5e-2; % tolerance + img_slice = zeros(dim1(1:2)); + TINY = 5e-2; % tolerance + + % Dimension of transformed 3D volume + % + xdim1 = dim1(1); + ydim1 = dim1(2); - % Dimension of transformed 3D volume - % - xdim1 = dim1(1); - ydim1 = dim1(2); + % Dimension of original 3D volume + % + xdim2 = dim2(1); + ydim2 = dim2(2); + zdim2 = dim2(3); - % Dimension of original 3D volume - % - xdim2 = dim2(1); - ydim2 = dim2(2); - zdim2 = dim2(3); + % initialize new_Y accumulation + % + Y2X = 0; + Y2Y = 0; + Y2Z = 0; - % initialize new_Y accumulation - % - Y2X = 0; - Y2Y = 0; - Y2Z = 0; + for y = 1:ydim1 - for y = 1:ydim1 + % increment of new_Y accumulation + % + Y2X = Y2X + M(1, 2); % new_Y to old_X + Y2Y = Y2Y + M(2, 2); % new_Y to old_Y + Y2Z = Y2Z + M(3, 2); % new_Y to old_Z - % increment of new_Y accumulation + % backproject new_Y accumulation and translation to old_XYZ + % + old_X = Y2X + M(1, 4); + old_Y = Y2Y + M(2, 4); + old_Z = Y2Z + M(3, 4); + + for x = 1:xdim1 + + % accumulate the increment of new_X, and apply it + % to the backprojected old_XYZ % - Y2X = Y2X + M(1,2); % new_Y to old_X - Y2Y = Y2Y + M(2,2); % new_Y to old_Y - Y2Z = Y2Z + M(3,2); % new_Y to old_Z + old_X = M(1, 1) + old_X; + old_Y = M(2, 1) + old_Y; + old_Z = M(3, 1) + old_Z; - % backproject new_Y accumulation and translation to old_XYZ + % within boundary of original image % - old_X = Y2X + M(1,4); - old_Y = Y2Y + M(2,4); - old_Z = Y2Z + M(3,4); - - for x = 1:xdim1 - - % accumulate the increment of new_X, and apply it - % to the backprojected old_XYZ - % - old_X = M(1,1) + old_X ; - old_Y = M(2,1) + old_Y ; - old_Z = M(3,1) + old_Z ; - - % within boundary of original image - % - if ( old_X > 1-TINY & old_X < xdim2+TINY & ... - old_Y > 1-TINY & old_Y < ydim2+TINY & ... - old_Z > 1-TINY & old_Z < zdim2+TINY ) - - % Calculate distance of old_XYZ to its neighbors for - % weighted intensity average - % - dx = old_X - floor(old_X); - dy = old_Y - floor(old_Y); - dz = old_Z - floor(old_Z); - - x000 = floor(old_X); - x100 = x000 + 1; - - if floor(old_X) < 1 - x000 = 1; - x100 = x000; - elseif floor(old_X) > xdim2-1 - x000 = xdim2; - x100 = x000; - end - - x010 = x000; - x001 = x000; - x011 = x000; - - x110 = x100; - x101 = x100; - x111 = x100; - - y000 = floor(old_Y); - y010 = y000 + 1; - - if floor(old_Y) < 1 - y000 = 1; - y100 = y000; - elseif floor(old_Y) > ydim2-1 - y000 = ydim2; - y010 = y000; - end - - y100 = y000; - y001 = y000; - y101 = y000; - - y110 = y010; - y011 = y010; - y111 = y010; - - z000 = floor(old_Z); - z001 = z000 + 1; - - if floor(old_Z) < 1 - z000 = 1; - z001 = z000; - elseif floor(old_Z) > zdim2-1 - z000 = zdim2; - z001 = z000; - end - - z100 = z000; - z010 = z000; - z110 = z000; - - z101 = z001; - z011 = z001; - z111 = z001; - - x010 = x000; - x001 = x000; - x011 = x000; - - x110 = x100; - x101 = x100; - x111 = x100; - - v000 = double(img(x000, y000, z000)); - v010 = double(img(x010, y010, z010)); - v001 = double(img(x001, y001, z001)); - v011 = double(img(x011, y011, z011)); - - v100 = double(img(x100, y100, z100)); - v110 = double(img(x110, y110, z110)); - v101 = double(img(x101, y101, z101)); - v111 = double(img(x111, y111, z111)); - - img_slice(x,y) = v000*(1-dx)*(1-dy)*(1-dz) + ... - v010*(1-dx)*dy*(1-dz) + ... - v001*(1-dx)*(1-dy)*dz + ... - v011*(1-dx)*dy*dz + ... - v100*dx*(1-dy)*(1-dz) + ... - v110*dx*dy*(1-dz) + ... - v101*dx*(1-dy)*dz + ... - v111*dx*dy*dz; - - else - img_slice(x,y) = bg; - - end % if boundary - - end % for x - end % for y - - return; % trilinear - - -%-------------------------------------------------------------------- + if old_X > 1 - TINY & old_X < xdim2 + TINY & ... + old_Y > 1 - TINY & old_Y < ydim2 + TINY & ... + old_Z > 1 - TINY & old_Z < zdim2 + TINY + + % Calculate distance of old_XYZ to its neighbors for + % weighted intensity average + % + dx = old_X - floor(old_X); + dy = old_Y - floor(old_Y); + dz = old_Z - floor(old_Z); + + x000 = floor(old_X); + x100 = x000 + 1; + + if floor(old_X) < 1 + x000 = 1; + x100 = x000; + elseif floor(old_X) > xdim2 - 1 + x000 = xdim2; + x100 = x000; + end + + x010 = x000; + x001 = x000; + x011 = x000; + + x110 = x100; + x101 = x100; + x111 = x100; + + y000 = floor(old_Y); + y010 = y000 + 1; + + if floor(old_Y) < 1 + y000 = 1; + y100 = y000; + elseif floor(old_Y) > ydim2 - 1 + y000 = ydim2; + y010 = y000; + end + + y100 = y000; + y001 = y000; + y101 = y000; + + y110 = y010; + y011 = y010; + y111 = y010; + + z000 = floor(old_Z); + z001 = z000 + 1; + + if floor(old_Z) < 1 + z000 = 1; + z001 = z000; + elseif floor(old_Z) > zdim2 - 1 + z000 = zdim2; + z001 = z000; + end + + z100 = z000; + z010 = z000; + z110 = z000; + + z101 = z001; + z011 = z001; + z111 = z001; + + x010 = x000; + x001 = x000; + x011 = x000; + + x110 = x100; + x101 = x100; + x111 = x100; + + v000 = double(img(x000, y000, z000)); + v010 = double(img(x010, y010, z010)); + v001 = double(img(x001, y001, z001)); + v011 = double(img(x011, y011, z011)); + + v100 = double(img(x100, y100, z100)); + v110 = double(img(x110, y110, z110)); + v101 = double(img(x101, y101, z101)); + v111 = double(img(x111, y111, z111)); + + img_slice(x, y) = v000 * (1 - dx) * (1 - dy) * (1 - dz) + ... + v010 * (1 - dx) * dy * (1 - dz) + ... + v001 * (1 - dx) * (1 - dy) * dz + ... + v011 * (1 - dx) * dy * dz + ... + v100 * dx * (1 - dy) * (1 - dz) + ... + v110 * dx * dy * (1 - dz) + ... + v101 * dx * (1 - dy) * dz + ... + v111 * dx * dy * dz; + + else + img_slice(x, y) = bg; + + end % if boundary + + end % for x + end % for y + + return % trilinear + + % -------------------------------------------------------------------- function img_slice = nearest_neighbor(img, dim1, dim2, M, bg) - img_slice = zeros(dim1(1:2)); + img_slice = zeros(dim1(1:2)); - % Dimension of transformed 3D volume - % - xdim1 = dim1(1); - ydim1 = dim1(2); + % Dimension of transformed 3D volume + % + xdim1 = dim1(1); + ydim1 = dim1(2); - % Dimension of original 3D volume - % - xdim2 = dim2(1); - ydim2 = dim2(2); - zdim2 = dim2(3); + % Dimension of original 3D volume + % + xdim2 = dim2(1); + ydim2 = dim2(2); + zdim2 = dim2(3); - % initialize new_Y accumulation - % - Y2X = 0; - Y2Y = 0; - Y2Z = 0; + % initialize new_Y accumulation + % + Y2X = 0; + Y2Y = 0; + Y2Z = 0; - for y = 1:ydim1 + for y = 1:ydim1 - % increment of new_Y accumulation - % - Y2X = Y2X + M(1,2); % new_Y to old_X - Y2Y = Y2Y + M(2,2); % new_Y to old_Y - Y2Z = Y2Z + M(3,2); % new_Y to old_Z + % increment of new_Y accumulation + % + Y2X = Y2X + M(1, 2); % new_Y to old_X + Y2Y = Y2Y + M(2, 2); % new_Y to old_Y + Y2Z = Y2Z + M(3, 2); % new_Y to old_Z - % backproject new_Y accumulation and translation to old_XYZ - % - old_X = Y2X + M(1,4); - old_Y = Y2Y + M(2,4); - old_Z = Y2Z + M(3,4); + % backproject new_Y accumulation and translation to old_XYZ + % + old_X = Y2X + M(1, 4); + old_Y = Y2Y + M(2, 4); + old_Z = Y2Z + M(3, 4); - for x = 1:xdim1 + for x = 1:xdim1 - % accumulate the increment of new_X and apply it - % to the backprojected old_XYZ - % - old_X = M(1,1) + old_X ; - old_Y = M(2,1) + old_Y ; - old_Z = M(3,1) + old_Z ; - - xi = round(old_X); - yi = round(old_Y); - zi = round(old_Z); + % accumulate the increment of new_X and apply it + % to the backprojected old_XYZ + % + old_X = M(1, 1) + old_X; + old_Y = M(2, 1) + old_Y; + old_Z = M(3, 1) + old_Z; - % within boundary of original image - % - if ( xi >= 1 & xi <= xdim2 & ... - yi >= 1 & yi <= ydim2 & ... - zi >= 1 & zi <= zdim2 ) + xi = round(old_X); + yi = round(old_Y); + zi = round(old_Z); - img_slice(x,y) = img(xi,yi,zi); + % within boundary of original image + % + if xi >= 1 & xi <= xdim2 & ... + yi >= 1 & yi <= ydim2 & ... + zi >= 1 & zi <= zdim2 - else - img_slice(x,y) = bg; + img_slice(x, y) = img(xi, yi, zi); - end % if boundary + else + img_slice(x, y) = bg; - end % for x - end % for y + end % if boundary - return; % nearest_neighbor + end % for x + end % for y + return % nearest_neighbor -%-------------------------------------------------------------------- + % -------------------------------------------------------------------- function img_slice = bresenham(img, dim1, dim2, M, bg) - img_slice = zeros(dim1(1:2)); + img_slice = zeros(dim1(1:2)); - % Dimension of transformed 3D volume - % - xdim1 = dim1(1); - ydim1 = dim1(2); + % Dimension of transformed 3D volume + % + xdim1 = dim1(1); + ydim1 = dim1(2); - % Dimension of original 3D volume - % - xdim2 = dim2(1); - ydim2 = dim2(2); - zdim2 = dim2(3); + % Dimension of original 3D volume + % + xdim2 = dim2(1); + ydim2 = dim2(2); + zdim2 = dim2(3); - for y = 1:ydim1 + for y = 1:ydim1 - start_old_XYZ = round(M*[0 y 0 1]'); - end_old_XYZ = round(M*[xdim1 y 0 1]'); + start_old_XYZ = round(M * [0 y 0 1]'); + end_old_XYZ = round(M * [xdim1 y 0 1]'); - [X Y Z] = bresenham_line3d(start_old_XYZ, end_old_XYZ); + [X Y Z] = bresenham_line3d(start_old_XYZ, end_old_XYZ); - % line error correction - % -% del = end_old_XYZ - start_old_XYZ; - % del_dom = max(del); - % idx_dom = find(del==del_dom); - % idx_dom = idx_dom(1); + % line error correction + % + % del = end_old_XYZ - start_old_XYZ; + % del_dom = max(del); + % idx_dom = find(del==del_dom); + % idx_dom = idx_dom(1); % idx_other = [1 2 3]; - % idx_other(idx_dom) = []; - %del_x1 = del(idx_other(1)); -% del_x2 = del(idx_other(2)); - % line_slope = sqrt((del_x1/del_dom)^2 + (del_x2/del_dom)^2 + 1); - % line_error = line_slope - 1; -% line error correction removed because it is too slow + % idx_other(idx_dom) = []; + % del_x1 = del(idx_other(1)); + % del_x2 = del(idx_other(2)); + % line_slope = sqrt((del_x1/del_dom)^2 + (del_x2/del_dom)^2 + 1); + % line_error = line_slope - 1; + % line error correction removed because it is too slow - for x = 1:xdim1 + for x = 1:xdim1 - % rescale ratio - % - i = round(x * length(X) / xdim1); - - if i < 1 - i = 1; - elseif i > length(X) - i = length(X); - end + % rescale ratio + % + i = round(x * length(X) / xdim1); - xi = X(i); - yi = Y(i); - zi = Z(i); + if i < 1 + i = 1; + elseif i > length(X) + i = length(X); + end - % within boundary of the old XYZ space - % - if ( xi >= 1 & xi <= xdim2 & ... - yi >= 1 & yi <= ydim2 & ... - zi >= 1 & zi <= zdim2 ) + xi = X(i); + yi = Y(i); + zi = Z(i); - img_slice(x,y) = img(xi,yi,zi); + % within boundary of the old XYZ space + % + if xi >= 1 & xi <= xdim2 & ... + yi >= 1 & yi <= ydim2 & ... + zi >= 1 & zi <= zdim2 -% if line_error > 1 - % x = x + 1; + img_slice(x, y) = img(xi, yi, zi); -% if x <= xdim1 - % img_slice(x,y) = img(xi,yi,zi); - % line_error = line_slope - 1; - % end - % end % if line_error -% line error correction removed because it is too slow + % if line_error > 1 + % x = x + 1; - else - img_slice(x,y) = bg; + % if x <= xdim1 + % img_slice(x,y) = img(xi,yi,zi); + % line_error = line_slope - 1; + % end + % end % if line_error + % line error correction removed because it is too slow - end % if boundary + else + img_slice(x, y) = bg; - end % for x - end % for y + end % if boundary - return; % bresenham + end % for x + end % for y + return % bresenham diff --git a/lib/NiftiTools/bipolar.m b/lib/NiftiTools/bipolar.m index 0c79c373..eda733df 100644 --- a/lib/NiftiTools/bipolar.m +++ b/lib/NiftiTools/bipolar.m @@ -1,94 +1,93 @@ -%BIPOLAR returns an M-by-3 matrix containing a blue-red colormap, in -% in which red stands for positive, blue stands for negative, -% and white stands for 0. +% BIPOLAR returns an M-by-3 matrix containing a blue-red colormap, in +% in which red stands for positive, blue stands for negative, +% and white stands for 0. % % Usage: cmap = bipolar(M, lo, hi, contrast); or cmap = bipolar; % % cmap: output M-by-3 matrix for BIPOLAR colormap. -% M: number of shades in the colormap. By default, it is the -% same length as the current colormap. -% lo: the lowest value to represent. -% hi: the highest value to represent. +% M: number of shades in the colormap. By default, it is the +% same length as the current colormap. +% lo: the lowest value to represent. +% hi: the highest value to represent. % % Inspired from the LORETA PASCAL program: -% http://www.unizh.ch/keyinst/NewLORETA +% http://www.unizh.ch/keyinst/NewLORETA % % jimmy@rotman-baycrest.on.ca % -%---------------------------------------------------------------- +% ---------------------------------------------------------------- function cmap = bipolar(M, lo, hi, contrast) - if ~exist('contrast','var') - contrast = 128; - end + if ~exist('contrast', 'var') + contrast = 128; + end - if ~exist('lo','var') - lo = -1; - end + if ~exist('lo', 'var') + lo = -1; + end - if ~exist('hi','var') - hi = 1; - end + if ~exist('hi', 'var') + hi = 1; + end - if ~exist('M','var') - cmap = colormap; - M = size(cmap,1); - end + if ~exist('M', 'var') + cmap = colormap; + M = size(cmap, 1); + end - steepness = 10 ^ (1 - (contrast-1)/127); - pos_infs = 1e-99; - neg_infs = -1e-99; + steepness = 10^(1 - (contrast - 1) / 127); + pos_infs = 1e-99; + neg_infs = -1e-99; - doubleredc = []; - doublebluec = []; + doubleredc = []; + doublebluec = []; - if lo >= 0 % all positive + if lo >= 0 % all positive - if lo == 0 - lo = pos_infs; - end + if lo == 0 + lo = pos_infs; + end - for i=linspace(hi/M, hi, M) - t = exp(log(i/hi)*steepness); - doubleredc = [doubleredc; [(1-t)+t,(1-t)+0,(1-t)+0]]; - end + for i = linspace(hi / M, hi, M) + t = exp(log(i / hi) * steepness); + doubleredc = [doubleredc; [(1 - t) + t, (1 - t) + 0, (1 - t) + 0]]; + end - cmap = doubleredc; + cmap = doubleredc; - elseif hi <= 0 % all negative + elseif hi <= 0 % all negative - if hi == 0 - hi = neg_infs; - end + if hi == 0 + hi = neg_infs; + end - for i=linspace(abs(lo)/M, abs(lo), M) - t = exp(log(i/abs(lo))*steepness); - doublebluec = [doublebluec; [(1-t)+0,(1-t)+0,(1-t)+t]]; - end + for i = linspace(abs(lo) / M, abs(lo), M) + t = exp(log(i / abs(lo)) * steepness); + doublebluec = [doublebluec; [(1 - t) + 0, (1 - t) + 0, (1 - t) + t]]; + end - cmap = flipud(doublebluec); + cmap = flipud(doublebluec); - else + else - if hi > abs(lo) - maxc = hi; - else - maxc = abs(lo); - end + if hi > abs(lo) + maxc = hi; + else + maxc = abs(lo); + end - for i=linspace(maxc/M, hi, round(M*hi/(hi-lo))) - t = exp(log(i/maxc)*steepness); - doubleredc = [doubleredc; [(1-t)+t,(1-t)+0,(1-t)+0]]; - end + for i = linspace(maxc / M, hi, round(M * hi / (hi - lo))) + t = exp(log(i / maxc) * steepness); + doubleredc = [doubleredc; [(1 - t) + t, (1 - t) + 0, (1 - t) + 0]]; + end - for i=linspace(maxc/M, abs(lo), round(M*abs(lo)/(hi-lo))) - t = exp(log(i/maxc)*steepness); - doublebluec = [doublebluec; [(1-t)+0,(1-t)+0,(1-t)+t]]; - end + for i = linspace(maxc / M, abs(lo), round(M * abs(lo) / (hi - lo))) + t = exp(log(i / maxc) * steepness); + doublebluec = [doublebluec; [(1 - t) + 0, (1 - t) + 0, (1 - t) + t]]; + end - cmap = [flipud(doublebluec); doubleredc]; + cmap = [flipud(doublebluec); doubleredc]; - end - - return; % bipolar + end + return % bipolar diff --git a/lib/NiftiTools/bresenham_line3d.m b/lib/NiftiTools/bresenham_line3d.m index d4db692a..29d828c7 100644 --- a/lib/NiftiTools/bresenham_line3d.m +++ b/lib/NiftiTools/bresenham_line3d.m @@ -9,34 +9,34 @@ % % Usage: [X Y Z] = bresenham_line3d(P1, P2, [precision]); % -% P1 - vector for Point1, where P1 = [x1 y1 z1] +% P1 - vector for Point1, where P1 = [x1 y1 z1] % -% P2 - vector for Point2, where P2 = [x2 y2 z2] +% P2 - vector for Point2, where P2 = [x2 y2 z2] % % precision (optional) - Although according to Bresenham's line -% algorithm, point coordinates x1 y1 z1 and x2 y2 z2 should -% be integer numbers, this program extends its limit to all -% real numbers. If any of them are floating numbers, you -% should specify how many digits of decimal that you would -% like to preserve. Be aware that the length of output X Y -% Z coordinates will increase in 10 times for each decimal -% digit that you want to preserve. By default, the precision -% is 0, which means that they will be rounded to the nearest -% integer. +% algorithm, point coordinates x1 y1 z1 and x2 y2 z2 should +% be integer numbers, this program extends its limit to all +% real numbers. If any of them are floating numbers, you +% should specify how many digits of decimal that you would +% like to preserve. Be aware that the length of output X Y +% Z coordinates will increase in 10 times for each decimal +% digit that you want to preserve. By default, the precision +% is 0, which means that they will be rounded to the nearest +% integer. % -% X - a set of x coordinates on Bresenham's line +% X - a set of x coordinates on Bresenham's line % -% Y - a set of y coordinates on Bresenham's line +% Y - a set of y coordinates on Bresenham's line % -% Z - a set of z coordinates on Bresenham's line +% Z - a set of z coordinates on Bresenham's line % % Therefore, all points in XYZ set (i.e. P(i) = [X(i) Y(i) Z(i)]) % will constitute the Bresenham's line between P1 and P1. % % Example: -% P1 = [12 37 6]; P2 = [46 3 35]; -% [X Y Z] = bresenham_line3d(P1, P2); -% figure; plot3(X,Y,Z,'s','markerface','b'); +% P1 = [12 37 6]; P2 = [46 3 35]; +% [X Y Z] = bresenham_line3d(P1, P2); +% figure; plot3(X,Y,Z,'s','markerface','b'); % % This program is ported to MATLAB from: % @@ -51,139 +51,138 @@ % % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % -function [X,Y,Z] = bresenham_line3d(P1, P2, precision) - - if ~exist('precision','var') | isempty(precision) | round(precision) == 0 - precision = 0; - P1 = round(P1); - P2 = round(P2); - else - precision = round(precision); - P1 = round(P1*(10^precision)); - P2 = round(P2*(10^precision)); - end - - d = max(abs(P2-P1)+1); - X = zeros(1, d); - Y = zeros(1, d); - Z = zeros(1, d); - - x1 = P1(1); - y1 = P1(2); - z1 = P1(3); - - x2 = P2(1); - y2 = P2(2); - z2 = P2(3); - - dx = x2 - x1; - dy = y2 - y1; - dz = z2 - z1; - - ax = abs(dx)*2; - ay = abs(dy)*2; - az = abs(dz)*2; - - sx = sign(dx); - sy = sign(dy); - sz = sign(dz); - - x = x1; - y = y1; - z = z1; - idx = 1; - - if(ax>=max(ay,az)) % x dominant - yd = ay - ax/2; - zd = az - ax/2; - - while(1) - X(idx) = x; - Y(idx) = y; - Z(idx) = z; - idx = idx + 1; - - if(x == x2) % end - break; - end - - if(yd >= 0) % move along y - y = y + sy; - yd = yd - ax; - end - - if(zd >= 0) % move along z - z = z + sz; - zd = zd - ax; - end - - x = x + sx; % move along x - yd = yd + ay; - zd = zd + az; +function [X, Y, Z] = bresenham_line3d(P1, P2, precision) + + if ~exist('precision', 'var') | isempty(precision) | round(precision) == 0 + precision = 0; + P1 = round(P1); + P2 = round(P2); + else + precision = round(precision); + P1 = round(P1 * (10^precision)); + P2 = round(P2 * (10^precision)); + end + + d = max(abs(P2 - P1) + 1); + X = zeros(1, d); + Y = zeros(1, d); + Z = zeros(1, d); + + x1 = P1(1); + y1 = P1(2); + z1 = P1(3); + + x2 = P2(1); + y2 = P2(2); + z2 = P2(3); + + dx = x2 - x1; + dy = y2 - y1; + dz = z2 - z1; + + ax = abs(dx) * 2; + ay = abs(dy) * 2; + az = abs(dz) * 2; + + sx = sign(dx); + sy = sign(dy); + sz = sign(dz); + + x = x1; + y = y1; + z = z1; + idx = 1; + + if ax >= max(ay, az) % x dominant + yd = ay - ax / 2; + zd = az - ax / 2; + + while 1 + X(idx) = x; + Y(idx) = y; + Z(idx) = z; + idx = idx + 1; + + if x == x2 % end + break end - elseif(ay>=max(ax,az)) % y dominant - xd = ax - ay/2; - zd = az - ay/2; - - while(1) - X(idx) = x; - Y(idx) = y; - Z(idx) = z; - idx = idx + 1; - - if(y == y2) % end - break; - end - - if(xd >= 0) % move along x - x = x + sx; - xd = xd - ay; - end - - if(zd >= 0) % move along z - z = z + sz; - zd = zd - ay; - end - - y = y + sy; % move along y - xd = xd + ax; - zd = zd + az; + + if yd >= 0 % move along y + y = y + sy; + yd = yd - ax; + end + + if zd >= 0 % move along z + z = z + sz; + zd = zd - ax; + end + + x = x + sx; % move along x + yd = yd + ay; + zd = zd + az; + end + elseif ay >= max(ax, az) % y dominant + xd = ax - ay / 2; + zd = az - ay / 2; + + while 1 + X(idx) = x; + Y(idx) = y; + Z(idx) = z; + idx = idx + 1; + + if y == y2 % end + break + end + + if xd >= 0 % move along x + x = x + sx; + xd = xd - ay; end - elseif(az>=max(ax,ay)) % z dominant - xd = ax - az/2; - yd = ay - az/2; - - while(1) - X(idx) = x; - Y(idx) = y; - Z(idx) = z; - idx = idx + 1; - - if(z == z2) % end - break; - end - - if(xd >= 0) % move along x - x = x + sx; - xd = xd - az; - end - - if(yd >= 0) % move along y - y = y + sy; - yd = yd - az; - end - - z = z + sz; % move along z - xd = xd + ax; - yd = yd + ay; + + if zd >= 0 % move along z + z = z + sz; + zd = zd - ay; + end + + y = y + sy; % move along y + xd = xd + ax; + zd = zd + az; + end + elseif az >= max(ax, ay) % z dominant + xd = ax - az / 2; + yd = ay - az / 2; + + while 1 + X(idx) = x; + Y(idx) = y; + Z(idx) = z; + idx = idx + 1; + + if z == z2 % end + break + end + + if xd >= 0 % move along x + x = x + sx; + xd = xd - az; + end + + if yd >= 0 % move along y + y = y + sy; + yd = yd - az; end - end - if precision ~= 0 - X = X/(10^precision); - Y = Y/(10^precision); - Z = Z/(10^precision); - end + z = z + sz; % move along z + xd = xd + ax; + yd = yd + ay; + end + end - return; % bresenham_line3d + if precision ~= 0 + X = X / (10^precision); + Y = Y / (10^precision); + Z = Z / (10^precision); + end + return % bresenham_line3d diff --git a/lib/NiftiTools/clip_nii.m b/lib/NiftiTools/clip_nii.m index 40fcb308..f36350d1 100644 --- a/lib/NiftiTools/clip_nii.m +++ b/lib/NiftiTools/clip_nii.m @@ -1,6 +1,6 @@ % CLIP_NII: Clip the NIfTI volume from any of the 6 sides % -% Usage: nii = clip_nii(nii, [option]) +% Usage: nii = clip_nii(nii, [option]) % % Inputs: % @@ -8,27 +8,27 @@ % % option - struct instructing how many voxel to be cut from which side. % -% option.cut_from_L = ( number of voxel ) -% option.cut_from_R = ( number of voxel ) -% option.cut_from_P = ( number of voxel ) -% option.cut_from_A = ( number of voxel ) -% option.cut_from_I = ( number of voxel ) -% option.cut_from_S = ( number of voxel ) +% option.cut_from_L = ( number of voxel ) +% option.cut_from_R = ( number of voxel ) +% option.cut_from_P = ( number of voxel ) +% option.cut_from_A = ( number of voxel ) +% option.cut_from_I = ( number of voxel ) +% option.cut_from_S = ( number of voxel ) % -% Options description in detail: -% ============================== +% Options description in detail: +% ============================== % -% cut_from_L: Number of voxels from Left side will be clipped. +% cut_from_L: Number of voxels from Left side will be clipped. % -% cut_from_R: Number of voxels from Right side will be clipped. +% cut_from_R: Number of voxels from Right side will be clipped. % -% cut_from_P: Number of voxels from Posterior side will be clipped. +% cut_from_P: Number of voxels from Posterior side will be clipped. % -% cut_from_A: Number of voxels from Anterior side will be clipped. +% cut_from_A: Number of voxels from Anterior side will be clipped. % -% cut_from_I: Number of voxels from Inferior side will be clipped. +% cut_from_I: Number of voxels from Inferior side will be clipped. % -% cut_from_S: Number of voxels from Superior side will be clipped. +% cut_from_S: Number of voxels from Superior side will be clipped. % % NIfTI data format can be found on: http://nifti.nimh.nih.gov % @@ -36,80 +36,79 @@ % function nii = clip_nii(nii, opt) - dims = abs(nii.hdr.dime.dim(2:4)); - origin = abs(nii.hdr.hist.originator(1:3)); + dims = abs(nii.hdr.dime.dim(2:4)); + origin = abs(nii.hdr.hist.originator(1:3)); - if isempty(origin) | all(origin == 0) % according to SPM - origin = round((dims+1)/2); - end + if isempty(origin) | all(origin == 0) % according to SPM + origin = round((dims + 1) / 2); + end - cut_from_L = 0; - cut_from_R = 0; - cut_from_P = 0; - cut_from_A = 0; - cut_from_I = 0; - cut_from_S = 0; + cut_from_L = 0; + cut_from_R = 0; + cut_from_P = 0; + cut_from_A = 0; + cut_from_I = 0; + cut_from_S = 0; - if nargin > 1 & ~isempty(opt) - if ~isstruct(opt) - error('option argument should be a struct'); - end + if nargin > 1 & ~isempty(opt) + if ~isstruct(opt) + error('option argument should be a struct'); + end - if isfield(opt,'cut_from_L') - cut_from_L = round(opt.cut_from_L); + if isfield(opt, 'cut_from_L') + cut_from_L = round(opt.cut_from_L); - if cut_from_L >= origin(1) | cut_from_L < 0 - error('cut_from_L cannot be negative or cut beyond originator'); - end + if cut_from_L >= origin(1) | cut_from_L < 0 + error('cut_from_L cannot be negative or cut beyond originator'); end + end - if isfield(opt,'cut_from_P') - cut_from_P = round(opt.cut_from_P); + if isfield(opt, 'cut_from_P') + cut_from_P = round(opt.cut_from_P); - if cut_from_P >= origin(2) | cut_from_P < 0 - error('cut_from_P cannot be negative or cut beyond originator'); - end + if cut_from_P >= origin(2) | cut_from_P < 0 + error('cut_from_P cannot be negative or cut beyond originator'); end + end - if isfield(opt,'cut_from_I') - cut_from_I = round(opt.cut_from_I); + if isfield(opt, 'cut_from_I') + cut_from_I = round(opt.cut_from_I); - if cut_from_I >= origin(3) | cut_from_I < 0 - error('cut_from_I cannot be negative or cut beyond originator'); - end + if cut_from_I >= origin(3) | cut_from_I < 0 + error('cut_from_I cannot be negative or cut beyond originator'); end + end - if isfield(opt,'cut_from_R') - cut_from_R = round(opt.cut_from_R); + if isfield(opt, 'cut_from_R') + cut_from_R = round(opt.cut_from_R); - if cut_from_R > dims(1)-origin(1) | cut_from_R < 0 - error('cut_from_R cannot be negative or cut beyond originator'); - end + if cut_from_R > dims(1) - origin(1) | cut_from_R < 0 + error('cut_from_R cannot be negative or cut beyond originator'); end + end - if isfield(opt,'cut_from_A') - cut_from_A = round(opt.cut_from_A); + if isfield(opt, 'cut_from_A') + cut_from_A = round(opt.cut_from_A); - if cut_from_A > dims(2)-origin(2) | cut_from_A < 0 - error('cut_from_A cannot be negative or cut beyond originator'); - end + if cut_from_A > dims(2) - origin(2) | cut_from_A < 0 + error('cut_from_A cannot be negative or cut beyond originator'); end + end - if isfield(opt,'cut_from_S') - cut_from_S = round(opt.cut_from_S); + if isfield(opt, 'cut_from_S') + cut_from_S = round(opt.cut_from_S); - if cut_from_S > dims(3)-origin(3) | cut_from_S < 0 - error('cut_from_S cannot be negative or cut beyond originator'); - end + if cut_from_S > dims(3) - origin(3) | cut_from_S < 0 + error('cut_from_S cannot be negative or cut beyond originator'); end - end - - nii = make_nii(nii.img( (cut_from_L+1) : (dims(1)-cut_from_R), ... - (cut_from_P+1) : (dims(2)-cut_from_A), ... - (cut_from_I+1) : (dims(3)-cut_from_S), ... - :,:,:,:,:), nii.hdr.dime.pixdim(2:4), ... - [origin(1)-cut_from_L origin(2)-cut_from_P origin(3)-cut_from_I], ... - nii.hdr.dime.datatype, nii.hdr.hist.descrip); + end + end - return; + nii = make_nii(nii.img((cut_from_L + 1):(dims(1) - cut_from_R), ... + (cut_from_P + 1):(dims(2) - cut_from_A), ... + (cut_from_I + 1):(dims(3) - cut_from_S), ... + :, :, :, :, :), nii.hdr.dime.pixdim(2:4), ... + [origin(1) - cut_from_L origin(2) - cut_from_P origin(3) - cut_from_I], ... + nii.hdr.dime.datatype, nii.hdr.hist.descrip); + return diff --git a/lib/NiftiTools/collapse_nii_scan.m b/lib/NiftiTools/collapse_nii_scan.m index 4c000787..ebec5f76 100644 --- a/lib/NiftiTools/collapse_nii_scan.m +++ b/lib/NiftiTools/collapse_nii_scan.m @@ -6,7 +6,7 @@ % If collapsed_fileprefix is omit, 'multi_scan' will be used % If scan_file_folder is omit, current file folder will be used % -% The order of volumes in the collapsed file will be the order of +% The order of volumes in the collapsed file will be the order of % corresponding filenames for those selected scan files. % % NIFTI data format can be found on: http://nifti.nimh.nih.gov @@ -15,246 +15,261 @@ % function collapse_nii_scan(scan_pattern, fileprefix, scan_path) - if ~exist('fileprefix','var') - fileprefix = 'multi_scan'; - else - [tmp fileprefix] = fileparts(fileprefix); - end - - if ~exist('scan_path','var'), scan_path = pwd; end - pnfn = fullfile(scan_path, scan_pattern); - - file_lst = dir(pnfn); - flist = {file_lst.name}; - flist = flist(:); - filename = flist{1}; - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - else - gzFile = 1; - end - else - if ~strcmp(filename(end-3:end), '.img') & ... - ~strcmp(filename(end-3:end), '.hdr') & ... - ~strcmp(filename(end-3:end), '.nii') - - error('Please check filename.'); - end - end - - nii = load_untouch_nii(fullfile(scan_path,filename)); - nii.hdr.dime.dim(5) = length(flist); - - if nii.hdr.dime.dim(1) < 4 - nii.hdr.dime.dim(1) = 4; - end - - hdr = nii.hdr; - filetype = nii.filetype; - - if isfield(nii,'ext') & ~isempty(nii.ext) - ext = nii.ext; - [ext, esize_total] = verify_nii_ext(ext); - else - ext = []; - end - - switch double(hdr.dime.datatype), - case 1, - hdr.dime.bitpix = int16(1 ); precision = 'ubit1'; - case 2, - hdr.dime.bitpix = int16(8 ); precision = 'uint8'; - case 4, - hdr.dime.bitpix = int16(16); precision = 'int16'; - case 8, - hdr.dime.bitpix = int16(32); precision = 'int32'; - case 16, - hdr.dime.bitpix = int16(32); precision = 'float32'; - case 32, - hdr.dime.bitpix = int16(64); precision = 'float32'; - case 64, - hdr.dime.bitpix = int16(64); precision = 'float64'; - case 128, - hdr.dime.bitpix = int16(24); precision = 'uint8'; - case 256 - hdr.dime.bitpix = int16(8 ); precision = 'int8'; - case 512 - hdr.dime.bitpix = int16(16); precision = 'uint16'; - case 768 - hdr.dime.bitpix = int16(32); precision = 'uint32'; - case 1024 - hdr.dime.bitpix = int16(64); precision = 'int64'; - case 1280 - hdr.dime.bitpix = int16(64); precision = 'uint64'; - case 1792, - hdr.dime.bitpix = int16(128); precision = 'float64'; - otherwise + if ~exist('fileprefix', 'var') + fileprefix = 'multi_scan'; + else + [tmp fileprefix] = fileparts(fileprefix); + end + + if ~exist('scan_path', 'var') + scan_path = pwd; + end + pnfn = fullfile(scan_path, scan_pattern); + + file_lst = dir(pnfn); + flist = {file_lst.name}; + flist = flist(:); + filename = flist{1}; + + v = version; + + % Check file extension. If .gz, unpack it into temp folder + % + if length(filename) > 2 & strcmp(filename(end - 2:end), '.gz') + + if ~strcmp(filename(end - 6:end), '.img.gz') & ... + ~strcmp(filename(end - 6:end), '.hdr.gz') & ... + ~strcmp(filename(end - 6:end), '.nii.gz') + + error('Please check filename.'); + end + + if str2num(v(1:3)) < 7.1 | ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + else + gzFile = 1; + end + else + if ~strcmp(filename(end - 3:end), '.img') & ... + ~strcmp(filename(end - 3:end), '.hdr') & ... + ~strcmp(filename(end - 3:end), '.nii') + + error('Please check filename.'); + end + end + + nii = load_untouch_nii(fullfile(scan_path, filename)); + nii.hdr.dime.dim(5) = length(flist); + + if nii.hdr.dime.dim(1) < 4 + nii.hdr.dime.dim(1) = 4; + end + + hdr = nii.hdr; + filetype = nii.filetype; + + if isfield(nii, 'ext') & ~isempty(nii.ext) + ext = nii.ext; + [ext, esize_total] = verify_nii_ext(ext); + else + ext = []; + end + + switch double(hdr.dime.datatype) + case 1 + hdr.dime.bitpix = int16(1); + precision = 'ubit1'; + case 2 + hdr.dime.bitpix = int16(8); + precision = 'uint8'; + case 4 + hdr.dime.bitpix = int16(16); + precision = 'int16'; + case 8 + hdr.dime.bitpix = int16(32); + precision = 'int32'; + case 16 + hdr.dime.bitpix = int16(32); + precision = 'float32'; + case 32 + hdr.dime.bitpix = int16(64); + precision = 'float32'; + case 64 + hdr.dime.bitpix = int16(64); + precision = 'float64'; + case 128 + hdr.dime.bitpix = int16(24); + precision = 'uint8'; + case 256 + hdr.dime.bitpix = int16(8); + precision = 'int8'; + case 512 + hdr.dime.bitpix = int16(16); + precision = 'uint16'; + case 768 + hdr.dime.bitpix = int16(32); + precision = 'uint32'; + case 1024 + hdr.dime.bitpix = int16(64); + precision = 'int64'; + case 1280 + hdr.dime.bitpix = int16(64); + precision = 'uint64'; + case 1792 + hdr.dime.bitpix = int16(128); + precision = 'float64'; + otherwise error('This datatype is not supported'); - end - - if filetype == 2 - fid = fopen(sprintf('%s.nii',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.nii.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 352; - - if ~isempty(ext) - hdr.dime.vox_offset = hdr.dime.vox_offset + esize_total; - end + end - hdr.hist.magic = 'n+1'; - save_untouch_nii_hdr(hdr, fid); + if filetype == 2 + fid = fopen(sprintf('%s.nii', fileprefix), 'w'); - if ~isempty(ext) - save_nii_ext(ext, fid); - end - elseif filetype == 1 - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 0; - hdr.hist.magic = 'ni1'; - save_untouch_nii_hdr(hdr, fid); + if fid < 0 + msg = sprintf('Cannot open file %s.nii.', fileprefix); + error(msg); + end - if ~isempty(ext) - save_nii_ext(ext, fid); - end - - fclose(fid); - fid = fopen(sprintf('%s.img',fileprefix),'w'); - else - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - save_untouch0_nii_hdr(hdr, fid); - - fclose(fid); - fid = fopen(sprintf('%s.img',fileprefix),'w'); - end - - if filetype == 2 & isempty(ext) - skip_bytes = double(hdr.dime.vox_offset) - 348; - else - skip_bytes = 0; - end - - if skip_bytes - fwrite(fid, zeros(1,skip_bytes), 'uint8'); - end - - glmax = -inf; - glmin = inf; - - for i = 1:length(flist) - nii = load_untouch_nii(fullfile(scan_path,flist{i})); - - if double(hdr.dime.datatype) == 128 - - % RGB planes are expected to be in the 4th dimension of nii.img - % - if(size(nii.img,4)~=3) - error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); - end - - nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); - end + hdr.dime.vox_offset = 352; - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - real_img = real(nii.img(:))'; - nii.img = imag(nii.img(:))'; - nii.img = [real_img; nii.img]; - end + if ~isempty(ext) + hdr.dime.vox_offset = hdr.dime.vox_offset + esize_total; + end - if nii.hdr.dime.glmax > glmax - glmax = nii.hdr.dime.glmax; - end + hdr.hist.magic = 'n+1'; + save_untouch_nii_hdr(hdr, fid); - if nii.hdr.dime.glmin < glmin - glmin = nii.hdr.dime.glmin; - end + if ~isempty(ext) + save_nii_ext(ext, fid); + end + elseif filetype == 1 + fid = fopen(sprintf('%s.hdr', fileprefix), 'w'); - fwrite(fid, nii.img, precision); - end + if fid < 0 + msg = sprintf('Cannot open file %s.hdr.', fileprefix); + error(msg); + end - hdr.dime.glmax = round(glmax); - hdr.dime.glmin = round(glmin); + hdr.dime.vox_offset = 0; + hdr.hist.magic = 'ni1'; + save_untouch_nii_hdr(hdr, fid); - if filetype == 2 - fseek(fid, 140, 'bof'); - fwrite(fid, hdr.dime.glmax, 'int32'); - fwrite(fid, hdr.dime.glmin, 'int32'); - elseif filetype == 1 - fid2 = fopen(sprintf('%s.hdr',fileprefix),'w'); + if ~isempty(ext) + save_nii_ext(ext, fid); + end - if fid2 < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end + fclose(fid); + fid = fopen(sprintf('%s.img', fileprefix), 'w'); + else + fid = fopen(sprintf('%s.hdr', fileprefix), 'w'); - save_untouch_nii_hdr(hdr, fid2); + if fid < 0 + msg = sprintf('Cannot open file %s.hdr.', fileprefix); + error(msg); + end - if ~isempty(ext) - save_nii_ext(ext, fid2); - end + save_untouch0_nii_hdr(hdr, fid); - fclose(fid2); - else - fid2 = fopen(sprintf('%s.hdr',fileprefix),'w'); + fclose(fid); + fid = fopen(sprintf('%s.img', fileprefix), 'w'); + end - if fid2 < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end + if filetype == 2 & isempty(ext) + skip_bytes = double(hdr.dime.vox_offset) - 348; + else + skip_bytes = 0; + end - save_untouch0_nii_hdr(hdr, fid2); + if skip_bytes + fwrite(fid, zeros(1, skip_bytes), 'uint8'); + end - fclose(fid2); - end + glmax = -inf; + glmin = inf; - fclose(fid); + for i = 1:length(flist) + nii = load_untouch_nii(fullfile(scan_path, flist{i})); - % gzip output file if requested - % - if exist('gzFile', 'var') - if filetype == 1 - gzip([fileprefix, '.img']); - delete([fileprefix, '.img']); - gzip([fileprefix, '.hdr']); - delete([fileprefix, '.hdr']); - elseif filetype == 2 - gzip([fileprefix, '.nii']); - delete([fileprefix, '.nii']); - end; - end; + if double(hdr.dime.datatype) == 128 - return; % collapse_nii_scan + % RGB planes are expected to be in the 4th dimension of nii.img + % + if size(nii.img, 4) ~= 3 + error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); + end + nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); + end + + % For complex float32 or complex float64, voxel values + % include [real, imag] + % + if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 + real_img = real(nii.img(:))'; + nii.img = imag(nii.img(:))'; + nii.img = [real_img; nii.img]; + end + + if nii.hdr.dime.glmax > glmax + glmax = nii.hdr.dime.glmax; + end + + if nii.hdr.dime.glmin < glmin + glmin = nii.hdr.dime.glmin; + end + + fwrite(fid, nii.img, precision); + end + + hdr.dime.glmax = round(glmax); + hdr.dime.glmin = round(glmin); + + if filetype == 2 + fseek(fid, 140, 'bof'); + fwrite(fid, hdr.dime.glmax, 'int32'); + fwrite(fid, hdr.dime.glmin, 'int32'); + elseif filetype == 1 + fid2 = fopen(sprintf('%s.hdr', fileprefix), 'w'); + + if fid2 < 0 + msg = sprintf('Cannot open file %s.hdr.', fileprefix); + error(msg); + end + + save_untouch_nii_hdr(hdr, fid2); + + if ~isempty(ext) + save_nii_ext(ext, fid2); + end + + fclose(fid2); + else + fid2 = fopen(sprintf('%s.hdr', fileprefix), 'w'); + + if fid2 < 0 + msg = sprintf('Cannot open file %s.hdr.', fileprefix); + error(msg); + end + + save_untouch0_nii_hdr(hdr, fid2); + + fclose(fid2); + end + + fclose(fid); + + % gzip output file if requested + % + if exist('gzFile', 'var') + if filetype == 1 + gzip([fileprefix, '.img']); + delete([fileprefix, '.img']); + gzip([fileprefix, '.hdr']); + delete([fileprefix, '.hdr']); + elseif filetype == 2 + gzip([fileprefix, '.nii']); + delete([fileprefix, '.nii']); + end + end + + return % collapse_nii_scan diff --git a/lib/NiftiTools/expand_nii_scan.m b/lib/NiftiTools/expand_nii_scan.m index e3a3bf88..3488266a 100644 --- a/lib/NiftiTools/expand_nii_scan.m +++ b/lib/NiftiTools/expand_nii_scan.m @@ -8,41 +8,44 @@ % function expand_nii_scan(filename, img_idx, newpath) - v = version; + v = version; - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') + % Check file extension. If .gz, unpack it into temp folder + % + if length(filename) > 2 & strcmp(filename(end - 2:end), '.gz') - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') + if ~strcmp(filename(end - 6:end), '.img.gz') & ... + ~strcmp(filename(end - 6:end), '.hdr.gz') & ... + ~strcmp(filename(end - 6:end), '.nii.gz') - error('Please check filename.'); - end + error('Please check filename.'); + end - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - else - gzFile = 1; - end - end + if str2num(v(1:3)) < 7.1 | ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + else + gzFile = 1; + end + end - if ~exist('newpath','var') | isempty(newpath), newpath = pwd; end - if ~exist('img_idx','var') | isempty(img_idx), img_idx = 1:get_nii_frame(filename); end + if ~exist('newpath', 'var') | isempty(newpath) + newpath = pwd; + end + if ~exist('img_idx', 'var') | isempty(img_idx) + img_idx = 1:get_nii_frame(filename); + end - for i=img_idx - nii_i = load_untouch_nii(filename, i); + for i = img_idx + nii_i = load_untouch_nii(filename, i); - fn = [nii_i.fileprefix '_' sprintf('%04d',i)]; - pnfn = fullfile(newpath, fn); + fn = [nii_i.fileprefix '_' sprintf('%04d', i)]; + pnfn = fullfile(newpath, fn); - if exist('gzFile', 'var') - pnfn = [pnfn '.nii.gz']; - end + if exist('gzFile', 'var') + pnfn = [pnfn '.nii.gz']; + end - save_untouch_nii(nii_i, pnfn); - end - - return; % expand_nii_scan + save_untouch_nii(nii_i, pnfn); + end + return % expand_nii_scan diff --git a/lib/NiftiTools/extra_nii_hdr.m b/lib/NiftiTools/extra_nii_hdr.m index 99a7f21c..df43916f 100644 --- a/lib/NiftiTools/extra_nii_hdr.m +++ b/lib/NiftiTools/extra_nii_hdr.m @@ -10,246 +10,245 @@ % function hdr = extra_nii_hdr(hdr) - switch hdr.dime.datatype - case 1 + switch hdr.dime.datatype + case 1 extra.NIFTI_DATATYPES = 'DT_BINARY'; - case 2 + case 2 extra.NIFTI_DATATYPES = 'DT_UINT8'; - case 4 + case 4 extra.NIFTI_DATATYPES = 'DT_INT16'; - case 8 + case 8 extra.NIFTI_DATATYPES = 'DT_INT32'; - case 16 + case 16 extra.NIFTI_DATATYPES = 'DT_FLOAT32'; - case 32 + case 32 extra.NIFTI_DATATYPES = 'DT_COMPLEX64'; - case 64 + case 64 extra.NIFTI_DATATYPES = 'DT_FLOAT64'; - case 128 + case 128 extra.NIFTI_DATATYPES = 'DT_RGB24'; - case 256 + case 256 extra.NIFTI_DATATYPES = 'DT_INT8'; - case 512 + case 512 extra.NIFTI_DATATYPES = 'DT_UINT16'; - case 768 + case 768 extra.NIFTI_DATATYPES = 'DT_UINT32'; - case 1024 + case 1024 extra.NIFTI_DATATYPES = 'DT_INT64'; - case 1280 + case 1280 extra.NIFTI_DATATYPES = 'DT_UINT64'; - case 1536 + case 1536 extra.NIFTI_DATATYPES = 'DT_FLOAT128'; - case 1792 + case 1792 extra.NIFTI_DATATYPES = 'DT_COMPLEX128'; - case 2048 + case 2048 extra.NIFTI_DATATYPES = 'DT_COMPLEX256'; - otherwise + otherwise extra.NIFTI_DATATYPES = 'DT_UNKNOWN'; - end + end - switch hdr.dime.intent_code - case 2 + switch hdr.dime.intent_code + case 2 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CORREL'; - case 3 + case 3 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_TTEST'; - case 4 + case 4 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_FTEST'; - case 5 + case 5 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_ZSCORE'; - case 6 + case 6 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CHISQ'; - case 7 + case 7 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_BETA'; - case 8 + case 8 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_BINOM'; - case 9 + case 9 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_GAMMA'; - case 10 + case 10 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_POISSON'; - case 11 + case 11 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_NORMAL'; - case 12 + case 12 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_FTEST_NONC'; - case 13 + case 13 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CHISQ_NONC'; - case 14 + case 14 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LOGISTIC'; - case 15 + case 15 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LAPLACE'; - case 16 + case 16 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_UNIFORM'; - case 17 + case 17 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_TTEST_NONC'; - case 18 + case 18 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_WEIBULL'; - case 19 + case 19 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CHI'; - case 20 + case 20 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_INVGAUSS'; - case 21 + case 21 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_EXTVAL'; - case 22 + case 22 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_PVAL'; - case 23 + case 23 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LOGPVAL'; - case 24 + case 24 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LOG10PVAL'; - case 1001 + case 1001 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_ESTIMATE'; - case 1002 + case 1002 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LABEL'; - case 1003 + case 1003 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_NEURONAME'; - case 1004 + case 1004 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_GENMATRIX'; - case 1005 + case 1005 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_SYMMATRIX'; - case 1006 + case 1006 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_DISPVECT'; - case 1007 + case 1007 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_VECTOR'; - case 1008 + case 1008 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_POINTSET'; - case 1009 + case 1009 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_TRIANGLE'; - case 1010 + case 1010 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_QUATERNION'; - case 1011 + case 1011 extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_DIMLESS'; - otherwise + otherwise extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_NONE'; - end + end - extra.NIFTI_INTENT_NAMES = hdr.hist.intent_name; + extra.NIFTI_INTENT_NAMES = hdr.hist.intent_name; - if hdr.hist.sform_code > 0 - switch hdr.hist.sform_code + if hdr.hist.sform_code > 0 + switch hdr.hist.sform_code case 1 - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_SCANNER_ANAT'; + extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_SCANNER_ANAT'; case 2 - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_ALIGNED_ANAT'; + extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_ALIGNED_ANAT'; case 3 - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_TALAIRACH'; + extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_TALAIRACH'; case 4 - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_MNI_152'; + extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_MNI_152'; otherwise - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - end + extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; + end - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - elseif hdr.hist.qform_code > 0 - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; + extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; + elseif hdr.hist.qform_code > 0 + extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - switch hdr.hist.qform_code + switch hdr.hist.qform_code case 1 - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_SCANNER_ANAT'; + extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_SCANNER_ANAT'; case 2 - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_ALIGNED_ANAT'; + extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_ALIGNED_ANAT'; case 3 - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_TALAIRACH'; + extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_TALAIRACH'; case 4 - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_MNI_152'; + extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_MNI_152'; otherwise - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - end - else - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - end - - switch bitand(hdr.dime.xyzt_units, 7) % mask with 0x07 - case 1 + extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; + end + else + extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; + extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; + end + + switch bitand(hdr.dime.xyzt_units, 7) % mask with 0x07 + case 1 extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_METER'; - case 2 - extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_MM'; % millimeter - case 3 + case 2 + extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_MM'; % millimeter + case 3 extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_MICRO'; - otherwise + otherwise extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_UNKNOWN'; - end + end - switch bitand(hdr.dime.xyzt_units, 56) % mask with 0x38 - case 8 + switch bitand(hdr.dime.xyzt_units, 56) % mask with 0x38 + case 8 extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_SEC'; - case 16 + case 16 extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_MSEC'; - case 24 - extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_USEC'; % microsecond - otherwise + case 24 + extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_USEC'; % microsecond + otherwise extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_UNKNOWN'; - end + end - switch hdr.dime.xyzt_units - case 32 + switch hdr.dime.xyzt_units + case 32 extra.NIFTI_SPECTRAL_UNIT = 'NIFTI_UNITS_HZ'; - case 40 - extra.NIFTI_SPECTRAL_UNIT = 'NIFTI_UNITS_PPM'; % part per million - case 48 - extra.NIFTI_SPECTRAL_UNIT = 'NIFTI_UNITS_RADS'; % radians per second - otherwise + case 40 + extra.NIFTI_SPECTRAL_UNIT = 'NIFTI_UNITS_PPM'; % part per million + case 48 + extra.NIFTI_SPECTRAL_UNIT = 'NIFTI_UNITS_RADS'; % radians per second + otherwise extra.NIFTI_SPECTRAL_UNIT = 'NIFTI_UNITS_UNKNOWN'; - end - - % MRI-specific spatial and temporal information - % - dim_info = hdr.hk.dim_info; - extra.NIFTI_FREQ_DIM = bitand(dim_info, 3); - extra.NIFTI_PHASE_DIM = bitand(bitshift(dim_info, -2), 3); - extra.NIFTI_SLICE_DIM = bitand(bitshift(dim_info, -4), 3); - - % Check slice code - % - switch hdr.dime.slice_code - case 1 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_SEQ_INC'; % sequential increasing - case 2 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_SEQ_DEC'; % sequential decreasing - case 3 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_INC'; % alternating increasing - case 4 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_DEC'; % alternating decreasing - case 5 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_INC2'; % ALT_INC # 2 - case 6 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_DEC2'; % ALT_DEC # 2 - otherwise + end + + % MRI-specific spatial and temporal information + % + dim_info = hdr.hk.dim_info; + extra.NIFTI_FREQ_DIM = bitand(dim_info, 3); + extra.NIFTI_PHASE_DIM = bitand(bitshift(dim_info, -2), 3); + extra.NIFTI_SLICE_DIM = bitand(bitshift(dim_info, -4), 3); + + % Check slice code + % + switch hdr.dime.slice_code + case 1 + extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_SEQ_INC'; % sequential increasing + case 2 + extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_SEQ_DEC'; % sequential decreasing + case 3 + extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_INC'; % alternating increasing + case 4 + extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_DEC'; % alternating decreasing + case 5 + extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_INC2'; % ALT_INC # 2 + case 6 + extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_DEC2'; % ALT_DEC # 2 + otherwise extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_UNKNOWN'; - end - - % Check NIFTI version - % - if ~isempty(hdr.hist.magic) & strcmp(hdr.hist.magic(1),'n') & ... - ( strcmp(hdr.hist.magic(2),'i') | strcmp(hdr.hist.magic(2),'+') ) & ... - str2num(hdr.hist.magic(3)) >= 1 & str2num(hdr.hist.magic(3)) <= 9 - - extra.NIFTI_VERSION = str2num(hdr.hist.magic(3)); - else - extra.NIFTI_VERSION = 0; - end - - % Check if data stored in the same file (*.nii) or separate - % files (*.hdr/*.img) - % - if isempty(hdr.hist.magic) - extra.NIFTI_ONEFILE = 0; - else - extra.NIFTI_ONEFILE = strcmp(hdr.hist.magic(2), '+'); - end - - % Swap has been taken care of by checking whether sizeof_hdr is - % 348 (machine is 'ieee-le' or 'ieee-be' etc) - % - % extra.NIFTI_NEEDS_SWAP = (hdr.dime.dim(1) < 0 | hdr.dime.dim(1) > 7); - - % Check NIFTI header struct contains a 5th (vector) dimension - % - if hdr.dime.dim(1) > 4 & hdr.dime.dim(6) > 1 - extra.NIFTI_5TH_DIM = hdr.dime.dim(6); - else - extra.NIFTI_5TH_DIM = 0; - end - - hdr.extra = extra; - - return; % extra_nii_hdr - + end + + % Check NIFTI version + % + if ~isempty(hdr.hist.magic) & strcmp(hdr.hist.magic(1), 'n') & ... + (strcmp(hdr.hist.magic(2), 'i') | strcmp(hdr.hist.magic(2), '+')) & ... + str2num(hdr.hist.magic(3)) >= 1 & str2num(hdr.hist.magic(3)) <= 9 + + extra.NIFTI_VERSION = str2num(hdr.hist.magic(3)); + else + extra.NIFTI_VERSION = 0; + end + + % Check if data stored in the same file (*.nii) or separate + % files (*.hdr/*.img) + % + if isempty(hdr.hist.magic) + extra.NIFTI_ONEFILE = 0; + else + extra.NIFTI_ONEFILE = strcmp(hdr.hist.magic(2), '+'); + end + + % Swap has been taken care of by checking whether sizeof_hdr is + % 348 (machine is 'ieee-le' or 'ieee-be' etc) + % + % extra.NIFTI_NEEDS_SWAP = (hdr.dime.dim(1) < 0 | hdr.dime.dim(1) > 7); + + % Check NIFTI header struct contains a 5th (vector) dimension + % + if hdr.dime.dim(1) > 4 & hdr.dime.dim(6) > 1 + extra.NIFTI_5TH_DIM = hdr.dime.dim(6); + else + extra.NIFTI_5TH_DIM = 0; + end + + hdr.extra = extra; + + return % extra_nii_hdr diff --git a/lib/NiftiTools/flip_lr.m b/lib/NiftiTools/flip_lr.m index 06543fd1..71324328 100644 --- a/lib/NiftiTools/flip_lr.m +++ b/lib/NiftiTools/flip_lr.m @@ -23,23 +23,23 @@ % flipped_fn - filename of the L-R flipped NIfTI file % % old_RGB (optional) - a scale number to tell difference of new RGB24 -% from old RGB24. New RGB24 uses RGB triple sequentially for each -% voxel, like [R1 G1 B1 R2 G2 B2 ...]. Analyze 6.0 from AnalyzeDirect -% uses old RGB24, in a way like [R1 R2 ... G1 G2 ... B1 B2 ...] for -% each slices. If the image that you view is garbled, try to set -% old_RGB variable to 1 and try again, because it could be in -% old RGB24. It will be set to 0, if it is default or empty. +% from old RGB24. New RGB24 uses RGB triple sequentially for each +% voxel, like [R1 G1 B1 R2 G2 B2 ...]. Analyze 6.0 from AnalyzeDirect +% uses old RGB24, in a way like [R1 R2 ... G1 G2 ... B1 B2 ...] for +% each slices. If the image that you view is garbled, try to set +% old_RGB variable to 1 and try again, because it could be in +% old RGB24. It will be set to 0, if it is default or empty. % % tolerance (optional) - distortion allowed for non-orthogonal rotation -% or shearing in NIfTI affine matrix. It will be set to 0.1 (10%), -% if it is default or empty. +% or shearing in NIfTI affine matrix. It will be set to 0.1 (10%), +% if it is default or empty. % % preferredForm (optional) - selects which transformation from voxels -% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate -% "prefer sform or qform, but use others if preferred not present". -% Upper case indicate the program is forced to use the specificied -% tranform or fail loading. 'preferredForm' will be 's', if it is -% default or empty. - Jeff Gunter +% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate +% "prefer sform or qform, but use others if preferred not present". +% Upper case indicate the program is forced to use the specificied +% tranform or fail loading. 'preferredForm' will be 's', if it is +% default or empty. - Jeff Gunter % % Example: flip_lr('avg152T1_LR_nifti.nii', 'flipped_lr.nii'); % flip_lr('avg152T1_RL_nifti.nii', 'flipped_rl.nii'); @@ -54,31 +54,30 @@ % function flip_lr(original_fn, flipped_fn, old_RGB, tolerance, preferredForm) - if ~exist('original_fn','var') | ~exist('flipped_fn','var') - error('Usage: flip_lr(original_fn, flipped_fn, [old_RGB],[tolerance])'); - end + if ~exist('original_fn', 'var') | ~exist('flipped_fn', 'var') + error('Usage: flip_lr(original_fn, flipped_fn, [old_RGB],[tolerance])'); + end - if ~exist('old_RGB','var') | isempty(old_RGB) - old_RGB = 0; - end + if ~exist('old_RGB', 'var') | isempty(old_RGB) + old_RGB = 0; + end - if ~exist('tolerance','var') | isempty(tolerance) - tolerance = 0.1; - end + if ~exist('tolerance', 'var') | isempty(tolerance) + tolerance = 0.1; + end - if ~exist('preferredForm','var') | isempty(preferredForm) - preferredForm= 's'; % Jeff - end + if ~exist('preferredForm', 'var') | isempty(preferredForm) + preferredForm = 's'; % Jeff + end - nii = load_nii(original_fn, [], [], [], [], old_RGB, tolerance, preferredForm); - M = diag(nii.hdr.dime.pixdim(2:5)); - M(1:3,4) = -M(1:3,1:3)*(nii.hdr.hist.originator(1:3)-1)'; - M(1,:) = -1*M(1,:); - nii.hdr.hist.sform_code = 1; - nii.hdr.hist.srow_x = M(1,:); - nii.hdr.hist.srow_y = M(2,:); - nii.hdr.hist.srow_z = M(3,:); - save_nii(nii, flipped_fn); - - return; % flip_lr + nii = load_nii(original_fn, [], [], [], [], old_RGB, tolerance, preferredForm); + M = diag(nii.hdr.dime.pixdim(2:5)); + M(1:3, 4) = -M(1:3, 1:3) * (nii.hdr.hist.originator(1:3) - 1)'; + M(1, :) = -1 * M(1, :); + nii.hdr.hist.sform_code = 1; + nii.hdr.hist.srow_x = M(1, :); + nii.hdr.hist.srow_y = M(2, :); + nii.hdr.hist.srow_z = M(3, :); + save_nii(nii, flipped_fn); + return % flip_lr diff --git a/lib/NiftiTools/get_nii_frame.m b/lib/NiftiTools/get_nii_frame.m index 154d5262..5236991a 100644 --- a/lib/NiftiTools/get_nii_frame.m +++ b/lib/NiftiTools/get_nii_frame.m @@ -1,10 +1,10 @@ -% Return time frame of a NIFTI dataset. Support both *.nii and +% Return time frame of a NIFTI dataset. Support both *.nii and % *.hdr/*.img file extension. If file extension is not provided, -% *.hdr/*.img will be used as default. +% *.hdr/*.img will be used as default. % % It is a lightweighted "load_nii_hdr", and is equivalent to % hdr.dime.dim(5) -% +% % Usage: [ total_scan ] = get_nii_frame(filename) % % filename - NIFTI file name. @@ -17,148 +17,148 @@ % % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % -function [ total_scan ] = get_nii_frame(filename) - - if ~exist('filename','var'), - error('Usage: [ total_scan ] = get_nii_frame(filename)'); - end - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - fileprefix = filename; - machine = 'ieee-le'; - new_ext = 0; - - if findstr('.nii',fileprefix) & strcmp(fileprefix(end-3:end), '.nii') - new_ext = 1; - fileprefix(end-3:end)=''; - end - - if findstr('.hdr',fileprefix) & strcmp(fileprefix(end-3:end), '.hdr') - fileprefix(end-3:end)=''; - end - - if findstr('.img',fileprefix) & strcmp(fileprefix(end-3:end), '.img') - fileprefix(end-3:end)=''; - end - - if new_ext - fn = sprintf('%s.nii',fileprefix); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.nii".', fileprefix); - error(msg); - end - else - fn = sprintf('%s.hdr',fileprefix); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.hdr".', fileprefix); - error(msg); - end - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); +function [total_scan] = get_nii_frame(filename) + + if ~exist('filename', 'var') + error('Usage: [ total_scan ] = get_nii_frame(filename)'); + end + + v = version; + + % Check file extension. If .gz, unpack it into temp folder + % + if length(filename) > 2 & strcmp(filename(end - 2:end), '.gz') + + if ~strcmp(filename(end - 6:end), '.img.gz') & ... + ~strcmp(filename(end - 6:end), '.hdr.gz') & ... + ~strcmp(filename(end - 6:end), '.nii.gz') + + error('Please check filename.'); + end + + if str2num(v(1:3)) < 7.1 | ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + elseif strcmp(filename(end - 6:end), '.img.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.hdr.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.hdr.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.img.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.nii.gz') + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + filename = gunzip(filename, tmpDir); + filename = char(filename); % convert from cell to string + end + end + + fileprefix = filename; + machine = 'ieee-le'; + new_ext = 0; + + if findstr('.nii', fileprefix) & strcmp(fileprefix(end - 3:end), '.nii') + new_ext = 1; + fileprefix(end - 3:end) = ''; + end + + if findstr('.hdr', fileprefix) & strcmp(fileprefix(end - 3:end), '.hdr') + fileprefix(end - 3:end) = ''; + end + + if findstr('.img', fileprefix) & strcmp(fileprefix(end - 3:end), '.img') + fileprefix(end - 3:end) = ''; + end + + if new_ext + fn = sprintf('%s.nii', fileprefix); + + if ~exist(fn) + msg = sprintf('Cannot find file "%s.nii".', fileprefix); + error(msg); + end + else + fn = sprintf('%s.hdr', fileprefix); + + if ~exist(fn) + msg = sprintf('Cannot find file "%s.hdr".', fileprefix); error(msg); - else + end + end + + fid = fopen(fn, 'r', machine); + + if fid < 0 + msg = sprintf('Cannot open file %s.', fn); + error(msg); + else + hdr = read_header(fid); + fclose(fid); + end + + if hdr.sizeof_hdr ~= 348 + % first try reading the opposite endian to 'machine' + switch machine + case 'ieee-le' + machine = 'ieee-be'; + case 'ieee-be' + machine = 'ieee-le'; + end + + fid = fopen(fn, 'r', machine); + + if fid < 0 + msg = sprintf('Cannot open file %s.', fn); + error(msg); + else hdr = read_header(fid); fclose(fid); - end - - if hdr.sizeof_hdr ~= 348 - % first try reading the opposite endian to 'machine' - switch machine, - case 'ieee-le', machine = 'ieee-be'; - case 'ieee-be', machine = 'ieee-le'; - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - hdr = read_header(fid); - fclose(fid); - end - end - - if hdr.sizeof_hdr ~= 348 - % Now throw an error - msg = sprintf('File "%s" is corrupted.',fn); - error(msg); - end - - total_scan = hdr.dim(5); + end + end - % Clean up after gunzip - % - if exist('gzFileName', 'var') - rmdir(tmpDir,'s'); - end + if hdr.sizeof_hdr ~= 348 + % Now throw an error + msg = sprintf('File "%s" is corrupted.', fn); + error(msg); + end - return; % get_nii_frame + total_scan = hdr.dim(5); + % Clean up after gunzip + % + if exist('gzFileName', 'var') + rmdir(tmpDir, 's'); + end -%--------------------------------------------------------------------- -function [ dsr ] = read_header(fid) + return % get_nii_frame - fseek(fid,0,'bof'); - dsr.sizeof_hdr = fread(fid,1,'int32')'; % should be 348! + % --------------------------------------------------------------------- +function [dsr] = read_header(fid) - fseek(fid,40,'bof'); - dsr.dim = fread(fid,8,'int16')'; + fseek(fid, 0, 'bof'); + dsr.sizeof_hdr = fread(fid, 1, 'int32')'; % should be 348! - return; % read_header + fseek(fid, 40, 'bof'); + dsr.dim = fread(fid, 8, 'int16')'; + return % read_header diff --git a/lib/NiftiTools/load_nii.m b/lib/NiftiTools/load_nii.m index c36ba77f..18294b91 100644 --- a/lib/NiftiTools/load_nii.m +++ b/lib/NiftiTools/load_nii.m @@ -9,190 +9,189 @@ % nii will be in RAS orientation, i.e. X axis from Left to Right, % Y axis from Posterior to Anterior, and Z axis from Inferior to % Superior. -% +% % Usage: nii = load_nii(filename, [img_idx], [dim5_idx], [dim6_idx], ... -% [dim7_idx], [old_RGB], [tolerance], [preferredForm]) -% -% filename - NIFTI or ANALYZE file name. -% +% [dim7_idx], [old_RGB], [tolerance], [preferredForm]) +% +% filename - NIFTI or ANALYZE file name. +% % img_idx (optional) - a numerical array of 4th dimension indices, -% which is the indices of image scan volume. The number of images -% scan volumes can be obtained from get_nii_frame.m, or simply -% hdr.dime.dim(5). Only the specified volumes will be loaded. -% All available image volumes will be loaded, if it is default or -% empty. +% which is the indices of image scan volume. The number of images +% scan volumes can be obtained from get_nii_frame.m, or simply +% hdr.dime.dim(5). Only the specified volumes will be loaded. +% All available image volumes will be loaded, if it is default or +% empty. % % dim5_idx (optional) - a numerical array of 5th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. +% Only the specified range will be loaded. All available range +% will be loaded, if it is default or empty. % % dim6_idx (optional) - a numerical array of 6th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. +% Only the specified range will be loaded. All available range +% will be loaded, if it is default or empty. % % dim7_idx (optional) - a numerical array of 7th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. +% Only the specified range will be loaded. All available range +% will be loaded, if it is default or empty. % % old_RGB (optional) - a scale number to tell difference of new RGB24 -% from old RGB24. New RGB24 uses RGB triple sequentially for each -% voxel, like [R1 G1 B1 R2 G2 B2 ...]. Analyze 6.0 from AnalyzeDirect -% uses old RGB24, in a way like [R1 R2 ... G1 G2 ... B1 B2 ...] for -% each slices. If the image that you view is garbled, try to set -% old_RGB variable to 1 and try again, because it could be in -% old RGB24. It will be set to 0, if it is default or empty. +% from old RGB24. New RGB24 uses RGB triple sequentially for each +% voxel, like [R1 G1 B1 R2 G2 B2 ...]. Analyze 6.0 from AnalyzeDirect +% uses old RGB24, in a way like [R1 R2 ... G1 G2 ... B1 B2 ...] for +% each slices. If the image that you view is garbled, try to set +% old_RGB variable to 1 and try again, because it could be in +% old RGB24. It will be set to 0, if it is default or empty. % % tolerance (optional) - distortion allowed in the loaded image for any -% non-orthogonal rotation or shearing of NIfTI affine matrix. If -% you set 'tolerance' to 0, it means that you do not allow any -% distortion. If you set 'tolerance' to 1, it means that you do -% not care any distortion. The image will fail to be loaded if it -% can not be tolerated. The tolerance will be set to 0.1 (10%), if -% it is default or empty. +% non-orthogonal rotation or shearing of NIfTI affine matrix. If +% you set 'tolerance' to 0, it means that you do not allow any +% distortion. If you set 'tolerance' to 1, it means that you do +% not care any distortion. The image will fail to be loaded if it +% can not be tolerated. The tolerance will be set to 0.1 (10%), if +% it is default or empty. % % preferredForm (optional) - selects which transformation from voxels -% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate -% "prefer sform or qform, but use others if preferred not present". -% Upper case indicate the program is forced to use the specificied -% tranform or fail loading. 'preferredForm' will be 's', if it is -% default or empty. - Jeff Gunter +% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate +% "prefer sform or qform, but use others if preferred not present". +% Upper case indicate the program is forced to use the specificied +% tranform or fail loading. 'preferredForm' will be 's', if it is +% default or empty. - Jeff Gunter % % Returned values: -% +% % nii structure: % -% hdr - struct with NIFTI header fields. +% hdr - struct with NIFTI header fields. % -% filetype - Analyze format .hdr/.img (0); -% NIFTI .hdr/.img (1); -% NIFTI .nii (2) +% filetype - Analyze format .hdr/.img (0); +% NIFTI .hdr/.img (1); +% NIFTI .nii (2) % -% fileprefix - NIFTI filename without extension. +% fileprefix - NIFTI filename without extension. % -% machine - machine string variable. +% machine - machine string variable. % -% img - 3D (or 4D) matrix of NIFTI data. +% img - 3D (or 4D) matrix of NIFTI data. +% +% original - the original header before any affine transform. % -% original - the original header before any affine transform. -% % Part of this file is copied and modified from: % http://www.mathworks.com/matlabcentral/fileexchange/1878-mri-analyze-tools -% +% % NIFTI data format can be found on: http://nifti.nimh.nih.gov -% +% % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % function nii = load_nii(filename, img_idx, dim5_idx, dim6_idx, dim7_idx, ... - old_RGB, tolerance, preferredForm) - - if ~exist('filename','var') - error('Usage: nii = load_nii(filename, [img_idx], [dim5_idx], [dim6_idx], [dim7_idx], [old_RGB], [tolerance], [preferredForm])'); - end - - if ~exist('img_idx','var') | isempty(img_idx) - img_idx = []; - end - - if ~exist('dim5_idx','var') | isempty(dim5_idx) - dim5_idx = []; - end - - if ~exist('dim6_idx','var') | isempty(dim6_idx) - dim6_idx = []; - end - - if ~exist('dim7_idx','var') | isempty(dim7_idx) - dim7_idx = []; - end - - if ~exist('old_RGB','var') | isempty(old_RGB) - old_RGB = 0; - end - - if ~exist('tolerance','var') | isempty(tolerance) - tolerance = 0.1; % 10 percent - end - - if ~exist('preferredForm','var') | isempty(preferredForm) - preferredForm= 's'; % Jeff - end - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - % Read the dataset header - % - [nii.hdr,nii.filetype,nii.fileprefix,nii.machine] = load_nii_hdr(filename); - - % Read the header extension - % -% nii.ext = load_nii_ext(filename); - - % Read the dataset body - % - [nii.img,nii.hdr] = load_nii_img(nii.hdr,nii.filetype,nii.fileprefix, ... - nii.machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB); - - % Perform some of sform/qform transform - % - nii = xform_nii(nii, tolerance, preferredForm); - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - - % fix fileprefix so it doesn't point to temp location - % - nii.fileprefix = gzFileName(1:end-7); - rmdir(tmpDir,'s'); - end - - return % load_nii - + old_RGB, tolerance, preferredForm) + + if ~exist('filename', 'var') + error('Usage: nii = load_nii(filename, [img_idx], [dim5_idx], [dim6_idx], [dim7_idx], [old_RGB], [tolerance], [preferredForm])'); + end + + if ~exist('img_idx', 'var') | isempty(img_idx) + img_idx = []; + end + + if ~exist('dim5_idx', 'var') | isempty(dim5_idx) + dim5_idx = []; + end + + if ~exist('dim6_idx', 'var') | isempty(dim6_idx) + dim6_idx = []; + end + + if ~exist('dim7_idx', 'var') | isempty(dim7_idx) + dim7_idx = []; + end + + if ~exist('old_RGB', 'var') | isempty(old_RGB) + old_RGB = 0; + end + + if ~exist('tolerance', 'var') | isempty(tolerance) + tolerance = 0.1; % 10 percent + end + + if ~exist('preferredForm', 'var') | isempty(preferredForm) + preferredForm = 's'; % Jeff + end + + v = version; + + % Check file extension. If .gz, unpack it into temp folder + % + if length(filename) > 2 & strcmp(filename(end - 2:end), '.gz') + + if ~strcmp(filename(end - 6:end), '.img.gz') & ... + ~strcmp(filename(end - 6:end), '.hdr.gz') & ... + ~strcmp(filename(end - 6:end), '.nii.gz') + + error('Please check filename.'); + end + + if str2num(v(1:3)) < 7.1 | ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + elseif strcmp(filename(end - 6:end), '.img.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.hdr.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.hdr.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.img.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.nii.gz') + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + filename = gunzip(filename, tmpDir); + filename = char(filename); % convert from cell to string + end + end + + % Read the dataset header + % + [nii.hdr, nii.filetype, nii.fileprefix, nii.machine] = load_nii_hdr(filename); + + % Read the header extension + % + % nii.ext = load_nii_ext(filename); + + % Read the dataset body + % + [nii.img, nii.hdr] = load_nii_img(nii.hdr, nii.filetype, nii.fileprefix, ... + nii.machine, img_idx, dim5_idx, dim6_idx, dim7_idx, old_RGB); + + % Perform some of sform/qform transform + % + nii = xform_nii(nii, tolerance, preferredForm); + + % Clean up after gunzip + % + if exist('gzFileName', 'var') + + % fix fileprefix so it doesn't point to temp location + % + nii.fileprefix = gzFileName(1:end - 7); + rmdir(tmpDir, 's'); + end + + return % load_nii diff --git a/lib/NiftiTools/load_nii_ext.m b/lib/NiftiTools/load_nii_ext.m index b588c251..616cafd7 100644 --- a/lib/NiftiTools/load_nii_ext.m +++ b/lib/NiftiTools/load_nii_ext.m @@ -18,189 +18,187 @@ % function ext = load_nii_ext(filename) - if ~exist('filename','var'), - error('Usage: ext = load_nii_ext(filename)'); - end - - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 && strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') && ... - ~strcmp(filename(end-6:end), '.hdr.gz') && ... - ~strcmp(filename(end-6:end), '.nii.gz') + if ~exist('filename', 'var') + error('Usage: ext = load_nii_ext(filename)'); + end + + v = version; + + % Check file extension. If .gz, unpack it into temp folder + % + if length(filename) > 2 && strcmp(filename(end - 2:end), '.gz') + + if ~strcmp(filename(end - 6:end), '.img.gz') && ... + ~strcmp(filename(end - 6:end), '.hdr.gz') && ... + ~strcmp(filename(end - 6:end), '.nii.gz') + + error('Please check filename.'); + end + + if str2num(v(1:3)) < 7.1 || ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + elseif strcmp(filename(end - 6:end), '.img.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.hdr.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.hdr.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.img.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.nii.gz') + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + filename = gunzip(filename, tmpDir); + filename = char(filename); % convert from cell to string + end + end + + machine = 'ieee-le'; + new_ext = 0; + + if all([strfind(filename, '.nii') strcmp(filename(end - 3:end), '.nii')]) + new_ext = 1; + filename(end - 3:end) = ''; + end + + if all([strfind(filename, '.hdr') strcmp(filename(end - 3:end), '.hdr')]) + filename(end - 3:end) = ''; + end + + if all([strfind(filename, '.img') strcmp(filename(end - 3:end), '.img')]) + filename(end - 3:end) = ''; + end + + if new_ext + fn = sprintf('%s.nii', filename); + + if ~exist(fn) + msg = sprintf('Cannot find file "%s.nii".', filename); + error(msg); + end + else + fn = sprintf('%s.hdr', filename); - error('Please check filename.'); + if ~exist(fn) + msg = sprintf('Cannot find file "%s.hdr".', filename); + error(msg); + end + end + + fid = fopen(fn, 'r', machine); + vox_offset = 0; + + if fid < 0 + msg = sprintf('Cannot open file %s.', fn); + error(msg); + else + fseek(fid, 0, 'bof'); + + if fread(fid, 1, 'int32') == 348 + if new_ext + fseek(fid, 108, 'bof'); + vox_offset = fread(fid, 1, 'float32'); end - if str2num(v(1:3)) < 7.1 || ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string + ext = read_extension(fid, vox_offset); + fclose(fid); + else + fclose(fid); + + % first try reading the opposite endian to 'machine' + % + switch machine + case 'ieee-le' + machine = 'ieee-be'; + case 'ieee-be' + machine = 'ieee-le'; end - end - - machine = 'ieee-le'; - new_ext = 0; - - if all([strfind(filename, '.nii') strcmp(filename(end-3:end), '.nii')]) - new_ext = 1; - filename(end-3:end)=''; - end - - if all([strfind(filename, '.hdr') strcmp(filename(end-3:end), '.hdr')]) - filename(end-3:end)=''; - end - - if all([strfind(filename, '.img') strcmp(filename(end-3:end), '.img')]) - filename(end-3:end)=''; - end - if new_ext - fn = sprintf('%s.nii',filename); + fid = fopen(fn, 'r', machine); - if ~exist(fn) - msg = sprintf('Cannot find file "%s.nii".', filename); - error(msg); - end - else - fn = sprintf('%s.hdr',filename); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.hdr".', filename); - error(msg); - end - end + if fid < 0 + msg = sprintf('Cannot open file %s.', fn); + error(msg); + else + fseek(fid, 0, 'bof'); - fid = fopen(fn,'r',machine); - vox_offset = 0; + if fread(fid, 1, 'int32') ~= 348 - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); + % Now throw an error + % + msg = sprintf('File "%s" is corrupted.', fn); + error(msg); + end - if fread(fid,1,'int32') == 348 - if new_ext - fseek(fid,108,'bof'); - vox_offset = fread(fid,1,'float32'); - end + if new_ext + fseek(fid, 108, 'bof'); + vox_offset = fread(fid, 1, 'float32'); + end - ext = read_extension(fid, vox_offset); - fclose(fid); - else - fclose(fid); - - % first try reading the opposite endian to 'machine' - % - switch machine, - case 'ieee-le', machine = 'ieee-be'; - case 'ieee-be', machine = 'ieee-le'; - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); - - if fread(fid,1,'int32') ~= 348 - - % Now throw an error - % - msg = sprintf('File "%s" is corrupted.',fn); - error(msg); - end - - if new_ext - fseek(fid,108,'bof'); - vox_offset = fread(fid,1,'float32'); - end - - ext = read_extension(fid, vox_offset); - fclose(fid); - end + ext = read_extension(fid, vox_offset); + fclose(fid); end - end - - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - rmdir(tmpDir,'s'); - end - + end + end - return % load_nii_ext + % Clean up after gunzip + % + if exist('gzFileName', 'var') + rmdir(tmpDir, 's'); + end + return % load_nii_ext -%--------------------------------------------------------------------- + % --------------------------------------------------------------------- function ext = read_extension(fid, vox_offset) - ext = []; + ext = []; - if vox_offset - end_of_ext = vox_offset; - else - fseek(fid, 0, 'eof'); - end_of_ext = ftell(fid); - end + if vox_offset + end_of_ext = vox_offset; + else + fseek(fid, 0, 'eof'); + end_of_ext = ftell(fid); + end - if end_of_ext > 352 - fseek(fid, 348, 'bof'); - ext.extension = fread(fid,4)'; - end + if end_of_ext > 352 + fseek(fid, 348, 'bof'); + ext.extension = fread(fid, 4)'; + end - if isempty(ext) || ext.extension(1) == 0 - ext = []; - return; - end + if isempty(ext) || ext.extension(1) == 0 + ext = []; + return + end - i = 1; + i = 1; - while(ftell(fid) < end_of_ext) - ext.section(i).esize = fread(fid,1,'int32'); - ext.section(i).ecode = fread(fid,1,'int32'); - ext.section(i).edata = char(fread(fid,ext.section(i).esize-8)'); - i = i + 1; - end + while ftell(fid) < end_of_ext + ext.section(i).esize = fread(fid, 1, 'int32'); + ext.section(i).ecode = fread(fid, 1, 'int32'); + ext.section(i).edata = char(fread(fid, ext.section(i).esize - 8)'); + i = i + 1; + end - ext.num_ext = length(ext.section); + ext.num_ext = length(ext.section); - return % read_extension + return % read_extension diff --git a/lib/NiftiTools/load_nii_hdr.m b/lib/NiftiTools/load_nii_hdr.m index 2494f1f4..4b51359e 100644 --- a/lib/NiftiTools/load_nii_hdr.m +++ b/lib/NiftiTools/load_nii_hdr.m @@ -4,276 +4,274 @@ function [hdr, filetype, fileprefix, machine] = load_nii_hdr(fileprefix) - if ~exist('fileprefix','var'), - error('Usage: [hdr, filetype, fileprefix, machine] = load_nii_hdr(filename)'); - end + if ~exist('fileprefix', 'var') + error('Usage: [hdr, filetype, fileprefix, machine] = load_nii_hdr(filename)'); + end - machine = 'ieee-le'; - new_ext = 0; + machine = 'ieee-le'; + new_ext = 0; - if all([strfind(fileprefix, '.nii') strcmp(fileprefix(end-3:end), '.nii')]) - new_ext = 1; - fileprefix(end-3:end)=''; - end + if all([strfind(fileprefix, '.nii') strcmp(fileprefix(end - 3:end), '.nii')]) + new_ext = 1; + fileprefix(end - 3:end) = ''; + end - if all([strfind(fileprefix, '.hdr') strcmp(fileprefix(end-3:end), '.hdr')]) - fileprefix(end-3:end)=''; - end + if all([strfind(fileprefix, '.hdr') strcmp(fileprefix(end - 3:end), '.hdr')]) + fileprefix(end - 3:end) = ''; + end - if all([strfind(fileprefix, '.img') strcmp(fileprefix(end-3:end), '.img')]) - fileprefix(end-3:end)=''; - end + if all([strfind(fileprefix, '.img') strcmp(fileprefix(end - 3:end), '.img')]) + fileprefix(end - 3:end) = ''; + end - if new_ext - fn = sprintf('%s.nii',fileprefix); + if new_ext + fn = sprintf('%s.nii', fileprefix); - if ~exist(fn) - msg = sprintf('Cannot find file "%s.nii".', fileprefix); - error(msg); - end - else - fn = sprintf('%s.hdr',fileprefix); + if ~exist(fn) + msg = sprintf('Cannot find file "%s.nii".', fileprefix); + error(msg); + end + else + fn = sprintf('%s.hdr', fileprefix); - if ~exist(fn) - msg = sprintf('Cannot find file "%s.hdr".', fileprefix); - error(msg); - end - end + if ~exist(fn) + msg = sprintf('Cannot find file "%s.hdr".', fileprefix); + error(msg); + end + end - fid = fopen(fn,'r',machine); + fid = fopen(fn, 'r', machine); - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); + if fid < 0 + msg = sprintf('Cannot open file %s.', fn); + error(msg); + else + fseek(fid, 0, 'bof'); - if fread(fid,1,'int32') == 348 - hdr = read_header(fid); - fclose(fid); - else - fclose(fid); - - % first try reading the opposite endian to 'machine' - % - switch machine, - case 'ieee-le', machine = 'ieee-be'; - case 'ieee-be', machine = 'ieee-le'; - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); - - if fread(fid,1,'int32') ~= 348 - - % Now throw an error - % - msg = sprintf('File "%s" is corrupted.',fn); - error(msg); - end - - hdr = read_header(fid); - fclose(fid); - end + if fread(fid, 1, 'int32') == 348 + hdr = read_header(fid); + fclose(fid); + else + fclose(fid); + + % first try reading the opposite endian to 'machine' + % + switch machine + case 'ieee-le' + machine = 'ieee-be'; + case 'ieee-be' + machine = 'ieee-le'; end - end - - if strcmp(hdr.hist.magic, 'n+1') - filetype = 2; - elseif strcmp(hdr.hist.magic, 'ni1') - filetype = 1; - else - filetype = 0; - end - - return % load_nii_hdr - - -%--------------------------------------------------------------------- -function [ dsr ] = read_header(fid) - - % Original header structures - % struct dsr - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ - - dsr.hk = header_key(fid); - dsr.dime = image_dimension(fid); - dsr.hist = data_history(fid); - - % For Analyze data format - % - if ~strcmp(dsr.hist.magic, 'n+1') && ~strcmp(dsr.hist.magic, 'ni1') - dsr.hist.qform_code = 0; - dsr.hist.sform_code = 0; - end - return % read_header - - -%--------------------------------------------------------------------- -function [ hk ] = header_key(fid) - - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char dim_info; % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - % - % int sizeof_header Should be 348. - % char regular Must be 'r' to indicate that all images and - % volumes are the same size. - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end + fid = fopen(fn, 'r', machine); - hk.sizeof_hdr = fread(fid, 1,'int32')'; % should be 348! - hk.data_type = deblank(fread(fid,10,directchar)'); - hk.db_name = deblank(fread(fid,18,directchar)'); - hk.extents = fread(fid, 1,'int32')'; - hk.session_error = fread(fid, 1,'int16')'; - hk.regular = fread(fid, 1,directchar)'; - hk.dim_info = fread(fid, 1,'uchar')'; - - return % header_key - - -%--------------------------------------------------------------------- -function [ dime ] = image_dimension(fid) - - % Original header structures - % struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % /* - % dim[0] Number of dimensions in database; usually 4. - % dim[1] Image X dimension; number of *pixels* in an image row. - % dim[2] Image Y dimension; number of *pixel rows* in slice. - % dim[3] Volume Z dimension; number of *slices* in a volume. - % dim[4] Time points; number of volumes in database - % */ - % float intent_p1; % char vox_units[4]; /* 16 + 4 */ - % float intent_p2; % char cal_units[8]; /* 20 + 4 */ - % float intent_p3; % char cal_units[8]; /* 24 + 4 */ - % short int intent_code; % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int slice_start; % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width, mm - % pixdim[2] - voxel height, mm - % pixdim[3] - slice thickness, mm - % pixdim[4] - volume timing, in msec - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float scl_slope; % float roi_scale; /* 72 + 4 */ - % float scl_inter; % float funused1; /* 76 + 4 */ - % short slice_end; % float funused2; /* 80 + 2 */ - % char slice_code; % float funused2; /* 82 + 1 */ - % char xyzt_units; % float funused2; /* 83 + 1 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % float slice_duration; % int compressed; /* 92 + 4 */ - % float toffset; % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - dime.dim = fread(fid,8,'int16')'; - dime.intent_p1 = fread(fid,1,'float32')'; - dime.intent_p2 = fread(fid,1,'float32')'; - dime.intent_p3 = fread(fid,1,'float32')'; - dime.intent_code = fread(fid,1,'int16')'; - dime.datatype = fread(fid,1,'int16')'; - dime.bitpix = fread(fid,1,'int16')'; - dime.slice_start = fread(fid,1,'int16')'; - dime.pixdim = fread(fid,8,'float32')'; - dime.vox_offset = fread(fid,1,'float32')'; - dime.scl_slope = fread(fid,1,'float32')'; - dime.scl_inter = fread(fid,1,'float32')'; - dime.slice_end = fread(fid,1,'int16')'; - dime.slice_code = fread(fid,1,'uchar')'; - dime.xyzt_units = fread(fid,1,'uchar')'; - dime.cal_max = fread(fid,1,'float32')'; - dime.cal_min = fread(fid,1,'float32')'; - dime.slice_duration = fread(fid,1,'float32')'; - dime.toffset = fread(fid,1,'float32')'; - dime.glmax = fread(fid,1,'int32')'; - dime.glmin = fread(fid,1,'int32')'; - - return % image_dimension - - -%--------------------------------------------------------------------- -function [ hist ] = data_history(fid) - - % Original header structures - % struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % short int qform_code; /* 104 + 2 */ - % short int sform_code; /* 106 + 2 */ - % float quatern_b; /* 108 + 4 */ - % float quatern_c; /* 112 + 4 */ - % float quatern_d; /* 116 + 4 */ - % float qoffset_x; /* 120 + 4 */ - % float qoffset_y; /* 124 + 4 */ - % float qoffset_z; /* 128 + 4 */ - % float srow_x[4]; /* 132 + 16 */ - % float srow_y[4]; /* 148 + 16 */ - % float srow_z[4]; /* 164 + 16 */ - % char intent_name[16]; /* 180 + 16 */ - % char magic[4]; % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end + if fid < 0 + msg = sprintf('Cannot open file %s.', fn); + error(msg); + else + fseek(fid, 0, 'bof'); - hist.descrip = deblank(fread(fid,80,directchar)'); - hist.aux_file = deblank(fread(fid,24,directchar)'); - hist.qform_code = fread(fid,1,'int16')'; - hist.sform_code = fread(fid,1,'int16')'; - hist.quatern_b = fread(fid,1,'float32')'; - hist.quatern_c = fread(fid,1,'float32')'; - hist.quatern_d = fread(fid,1,'float32')'; - hist.qoffset_x = fread(fid,1,'float32')'; - hist.qoffset_y = fread(fid,1,'float32')'; - hist.qoffset_z = fread(fid,1,'float32')'; - hist.srow_x = fread(fid,4,'float32')'; - hist.srow_y = fread(fid,4,'float32')'; - hist.srow_z = fread(fid,4,'float32')'; - hist.intent_name = deblank(fread(fid,16,directchar)'); - hist.magic = deblank(fread(fid,4,directchar)'); - - fseek(fid,253,'bof'); - hist.originator = fread(fid, 5,'int16')'; - - return % data_history + if fread(fid, 1, 'int32') ~= 348 + + % Now throw an error + % + msg = sprintf('File "%s" is corrupted.', fn); + error(msg); + end + + hdr = read_header(fid); + fclose(fid); + end + end + end + + if strcmp(hdr.hist.magic, 'n+1') + filetype = 2; + elseif strcmp(hdr.hist.magic, 'ni1') + filetype = 1; + else + filetype = 0; + end + + return % load_nii_hdr + + % --------------------------------------------------------------------- +function [dsr] = read_header(fid) + + % Original header structures + % struct dsr + % { + % struct header_key hk; /* 0 + 40 */ + % struct image_dimension dime; /* 40 + 108 */ + % struct data_history hist; /* 148 + 200 */ + % }; /* total= 348 bytes*/ + + dsr.hk = header_key(fid); + dsr.dime = image_dimension(fid); + dsr.hist = data_history(fid); + + % For Analyze data format + % + if ~strcmp(dsr.hist.magic, 'n+1') && ~strcmp(dsr.hist.magic, 'ni1') + dsr.hist.qform_code = 0; + dsr.hist.sform_code = 0; + end + + return % read_header + + % --------------------------------------------------------------------- +function [hk] = header_key(fid) + + fseek(fid, 0, 'bof'); + + % Original header structures + % struct header_key /* header key */ + % { /* off + size */ + % int sizeof_hdr /* 0 + 4 */ + % char data_type[10]; /* 4 + 10 */ + % char db_name[18]; /* 14 + 18 */ + % int extents; /* 32 + 4 */ + % short int session_error; /* 36 + 2 */ + % char regular; /* 38 + 1 */ + % char dim_info; % char hkey_un0; /* 39 + 1 */ + % }; /* total=40 bytes */ + % + % int sizeof_header Should be 348. + % char regular Must be 'r' to indicate that all images and + % volumes are the same size. + + v6 = version; + if str2num(v6(1)) < 6 + directchar = '*char'; + else + directchar = 'uchar=>char'; + end + + hk.sizeof_hdr = fread(fid, 1, 'int32')'; % should be 348! + hk.data_type = deblank(fread(fid, 10, directchar)'); + hk.db_name = deblank(fread(fid, 18, directchar)'); + hk.extents = fread(fid, 1, 'int32')'; + hk.session_error = fread(fid, 1, 'int16')'; + hk.regular = fread(fid, 1, directchar)'; + hk.dim_info = fread(fid, 1, 'uchar')'; + + return % header_key + + % --------------------------------------------------------------------- +function [dime] = image_dimension(fid) + + % Original header structures + % struct image_dimension + % { /* off + size */ + % short int dim[8]; /* 0 + 16 */ + % /* + % dim[0] Number of dimensions in database; usually 4. + % dim[1] Image X dimension; number of *pixels* in an image row. + % dim[2] Image Y dimension; number of *pixel rows* in slice. + % dim[3] Volume Z dimension; number of *slices* in a volume. + % dim[4] Time points; number of volumes in database + % */ + % float intent_p1; % char vox_units[4]; /* 16 + 4 */ + % float intent_p2; % char cal_units[8]; /* 20 + 4 */ + % float intent_p3; % char cal_units[8]; /* 24 + 4 */ + % short int intent_code; % short int unused1; /* 28 + 2 */ + % short int datatype; /* 30 + 2 */ + % short int bitpix; /* 32 + 2 */ + % short int slice_start; % short int dim_un0; /* 34 + 2 */ + % float pixdim[8]; /* 36 + 32 */ + % /* + % pixdim[] specifies the voxel dimensions: + % pixdim[1] - voxel width, mm + % pixdim[2] - voxel height, mm + % pixdim[3] - slice thickness, mm + % pixdim[4] - volume timing, in msec + % ..etc + % */ + % float vox_offset; /* 68 + 4 */ + % float scl_slope; % float roi_scale; /* 72 + 4 */ + % float scl_inter; % float funused1; /* 76 + 4 */ + % short slice_end; % float funused2; /* 80 + 2 */ + % char slice_code; % float funused2; /* 82 + 1 */ + % char xyzt_units; % float funused2; /* 83 + 1 */ + % float cal_max; /* 84 + 4 */ + % float cal_min; /* 88 + 4 */ + % float slice_duration; % int compressed; /* 92 + 4 */ + % float toffset; % int verified; /* 96 + 4 */ + % int glmax; /* 100 + 4 */ + % int glmin; /* 104 + 4 */ + % }; /* total=108 bytes */ + + dime.dim = fread(fid, 8, 'int16')'; + dime.intent_p1 = fread(fid, 1, 'float32')'; + dime.intent_p2 = fread(fid, 1, 'float32')'; + dime.intent_p3 = fread(fid, 1, 'float32')'; + dime.intent_code = fread(fid, 1, 'int16')'; + dime.datatype = fread(fid, 1, 'int16')'; + dime.bitpix = fread(fid, 1, 'int16')'; + dime.slice_start = fread(fid, 1, 'int16')'; + dime.pixdim = fread(fid, 8, 'float32')'; + dime.vox_offset = fread(fid, 1, 'float32')'; + dime.scl_slope = fread(fid, 1, 'float32')'; + dime.scl_inter = fread(fid, 1, 'float32')'; + dime.slice_end = fread(fid, 1, 'int16')'; + dime.slice_code = fread(fid, 1, 'uchar')'; + dime.xyzt_units = fread(fid, 1, 'uchar')'; + dime.cal_max = fread(fid, 1, 'float32')'; + dime.cal_min = fread(fid, 1, 'float32')'; + dime.slice_duration = fread(fid, 1, 'float32')'; + dime.toffset = fread(fid, 1, 'float32')'; + dime.glmax = fread(fid, 1, 'int32')'; + dime.glmin = fread(fid, 1, 'int32')'; + + return % image_dimension + + % --------------------------------------------------------------------- +function [hist] = data_history(fid) + + % Original header structures + % struct data_history + % { /* off + size */ + % char descrip[80]; /* 0 + 80 */ + % char aux_file[24]; /* 80 + 24 */ + % short int qform_code; /* 104 + 2 */ + % short int sform_code; /* 106 + 2 */ + % float quatern_b; /* 108 + 4 */ + % float quatern_c; /* 112 + 4 */ + % float quatern_d; /* 116 + 4 */ + % float qoffset_x; /* 120 + 4 */ + % float qoffset_y; /* 124 + 4 */ + % float qoffset_z; /* 128 + 4 */ + % float srow_x[4]; /* 132 + 16 */ + % float srow_y[4]; /* 148 + 16 */ + % float srow_z[4]; /* 164 + 16 */ + % char intent_name[16]; /* 180 + 16 */ + % char magic[4]; % int smin; /* 196 + 4 */ + % }; /* total=200 bytes */ + + v6 = version; + if str2num(v6(1)) < 6 + directchar = '*char'; + else + directchar = 'uchar=>char'; + end + + hist.descrip = deblank(fread(fid, 80, directchar)'); + hist.aux_file = deblank(fread(fid, 24, directchar)'); + hist.qform_code = fread(fid, 1, 'int16')'; + hist.sform_code = fread(fid, 1, 'int16')'; + hist.quatern_b = fread(fid, 1, 'float32')'; + hist.quatern_c = fread(fid, 1, 'float32')'; + hist.quatern_d = fread(fid, 1, 'float32')'; + hist.qoffset_x = fread(fid, 1, 'float32')'; + hist.qoffset_y = fread(fid, 1, 'float32')'; + hist.qoffset_z = fread(fid, 1, 'float32')'; + hist.srow_x = fread(fid, 4, 'float32')'; + hist.srow_y = fread(fid, 4, 'float32')'; + hist.srow_z = fread(fid, 4, 'float32')'; + hist.intent_name = deblank(fread(fid, 16, directchar)'); + hist.magic = deblank(fread(fid, 4, directchar)'); + + fseek(fid, 253, 'bof'); + hist.originator = fread(fid, 5, 'int16')'; + + return % data_history diff --git a/lib/NiftiTools/load_nii_img.m b/lib/NiftiTools/load_nii_img.m index 60adb7d1..d6d2af16 100644 --- a/lib/NiftiTools/load_nii_img.m +++ b/lib/NiftiTools/load_nii_img.m @@ -2,391 +2,404 @@ % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -function [img,hdr] = load_nii_img(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB) - - if ~exist('hdr','var') | ~exist('filetype','var') | ~exist('fileprefix','var') | ~exist('machine','var') - error('Usage: [img,hdr] = load_nii_img(hdr,filetype,fileprefix,machine,[img_idx],[dim5_idx],[dim6_idx],[dim7_idx],[old_RGB]);'); - end - - if ~exist('img_idx','var') | isempty(img_idx) | hdr.dime.dim(5)<1 - img_idx = []; - end - - if ~exist('dim5_idx','var') | isempty(dim5_idx) | hdr.dime.dim(6)<1 - dim5_idx = []; - end - - if ~exist('dim6_idx','var') | isempty(dim6_idx) | hdr.dime.dim(7)<1 - dim6_idx = []; - end - - if ~exist('dim7_idx','var') | isempty(dim7_idx) | hdr.dime.dim(8)<1 - dim7_idx = []; - end - - if ~exist('old_RGB','var') | isempty(old_RGB) - old_RGB = 0; - end - - % check img_idx - % - if ~isempty(img_idx) & ~isnumeric(img_idx) - error('"img_idx" should be a numerical array.'); - end - - if length(unique(img_idx)) ~= length(img_idx) - error('Duplicate image index in "img_idx"'); - end - - if ~isempty(img_idx) & (min(img_idx) < 1 | max(img_idx) > hdr.dime.dim(5)) - max_range = hdr.dime.dim(5); - - if max_range == 1 - error(['"img_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"img_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim5_idx - % - if ~isempty(dim5_idx) & ~isnumeric(dim5_idx) - error('"dim5_idx" should be a numerical array.'); - end - - if length(unique(dim5_idx)) ~= length(dim5_idx) - error('Duplicate index in "dim5_idx"'); - end - - if ~isempty(dim5_idx) & (min(dim5_idx) < 1 | max(dim5_idx) > hdr.dime.dim(6)) - max_range = hdr.dime.dim(6); - - if max_range == 1 - error(['"dim5_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim5_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim6_idx - % - if ~isempty(dim6_idx) & ~isnumeric(dim6_idx) - error('"dim6_idx" should be a numerical array.'); - end - - if length(unique(dim6_idx)) ~= length(dim6_idx) - error('Duplicate index in "dim6_idx"'); - end - - if ~isempty(dim6_idx) & (min(dim6_idx) < 1 | max(dim6_idx) > hdr.dime.dim(7)) - max_range = hdr.dime.dim(7); - - if max_range == 1 - error(['"dim6_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim6_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim7_idx - % - if ~isempty(dim7_idx) & ~isnumeric(dim7_idx) - error('"dim7_idx" should be a numerical array.'); - end - - if length(unique(dim7_idx)) ~= length(dim7_idx) - error('Duplicate index in "dim7_idx"'); - end - - if ~isempty(dim7_idx) & (min(dim7_idx) < 1 | max(dim7_idx) > hdr.dime.dim(8)) - max_range = hdr.dime.dim(8); - - if max_range == 1 - error(['"dim7_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim7_idx" should be an integer within the range of [' range '].']); - end - end - - [img,hdr] = read_image(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB); - - return % load_nii_img - - -%--------------------------------------------------------------------- -function [img,hdr] = read_image(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB) - - switch filetype - case {0, 1} +function [img, hdr] = load_nii_img(hdr, filetype, fileprefix, machine, img_idx, dim5_idx, dim6_idx, dim7_idx, old_RGB) + + if ~exist('hdr', 'var') | ~exist('filetype', 'var') | ~exist('fileprefix', 'var') | ~exist('machine', 'var') + error('Usage: [img,hdr] = load_nii_img(hdr,filetype,fileprefix,machine,[img_idx],[dim5_idx],[dim6_idx],[dim7_idx],[old_RGB]);'); + end + + if ~exist('img_idx', 'var') | isempty(img_idx) | hdr.dime.dim(5) < 1 + img_idx = []; + end + + if ~exist('dim5_idx', 'var') | isempty(dim5_idx) | hdr.dime.dim(6) < 1 + dim5_idx = []; + end + + if ~exist('dim6_idx', 'var') | isempty(dim6_idx) | hdr.dime.dim(7) < 1 + dim6_idx = []; + end + + if ~exist('dim7_idx', 'var') | isempty(dim7_idx) | hdr.dime.dim(8) < 1 + dim7_idx = []; + end + + if ~exist('old_RGB', 'var') | isempty(old_RGB) + old_RGB = 0; + end + + % check img_idx + % + if ~isempty(img_idx) & ~isnumeric(img_idx) + error('"img_idx" should be a numerical array.'); + end + + if length(unique(img_idx)) ~= length(img_idx) + error('Duplicate image index in "img_idx"'); + end + + if ~isempty(img_idx) & (min(img_idx) < 1 | max(img_idx) > hdr.dime.dim(5)) + max_range = hdr.dime.dim(5); + + if max_range == 1 + error(['"img_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"img_idx" should be an integer within the range of [' range '].']); + end + end + + % check dim5_idx + % + if ~isempty(dim5_idx) & ~isnumeric(dim5_idx) + error('"dim5_idx" should be a numerical array.'); + end + + if length(unique(dim5_idx)) ~= length(dim5_idx) + error('Duplicate index in "dim5_idx"'); + end + + if ~isempty(dim5_idx) & (min(dim5_idx) < 1 | max(dim5_idx) > hdr.dime.dim(6)) + max_range = hdr.dime.dim(6); + + if max_range == 1 + error(['"dim5_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"dim5_idx" should be an integer within the range of [' range '].']); + end + end + + % check dim6_idx + % + if ~isempty(dim6_idx) & ~isnumeric(dim6_idx) + error('"dim6_idx" should be a numerical array.'); + end + + if length(unique(dim6_idx)) ~= length(dim6_idx) + error('Duplicate index in "dim6_idx"'); + end + + if ~isempty(dim6_idx) & (min(dim6_idx) < 1 | max(dim6_idx) > hdr.dime.dim(7)) + max_range = hdr.dime.dim(7); + + if max_range == 1 + error(['"dim6_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"dim6_idx" should be an integer within the range of [' range '].']); + end + end + + % check dim7_idx + % + if ~isempty(dim7_idx) & ~isnumeric(dim7_idx) + error('"dim7_idx" should be a numerical array.'); + end + + if length(unique(dim7_idx)) ~= length(dim7_idx) + error('Duplicate index in "dim7_idx"'); + end + + if ~isempty(dim7_idx) & (min(dim7_idx) < 1 | max(dim7_idx) > hdr.dime.dim(8)) + max_range = hdr.dime.dim(8); + + if max_range == 1 + error(['"dim7_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"dim7_idx" should be an integer within the range of [' range '].']); + end + end + + [img, hdr] = read_image(hdr, filetype, fileprefix, machine, img_idx, dim5_idx, dim6_idx, dim7_idx, old_RGB); + + return % load_nii_img + + % --------------------------------------------------------------------- +function [img, hdr] = read_image(hdr, filetype, fileprefix, machine, img_idx, dim5_idx, dim6_idx, dim7_idx, old_RGB) + + switch filetype + case {0, 1} fn = [fileprefix '.img']; - case 2 + case 2 fn = [fileprefix '.nii']; - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - end - - % Set bitpix according to datatype - % - % /*Acceptable values for datatype are*/ - % - % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN - % 1 Binary (ubit1, bitpix=1) % DT_BINARY - % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 - % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 - % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 - % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 - % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 - % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 - % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 - % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 - % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 - % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 - % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 - % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 - % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 - % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 - % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % - switch hdr.dime.datatype - case 1, - hdr.dime.bitpix = 1; precision = 'ubit1'; - case 2, - hdr.dime.bitpix = 8; precision = 'uint8'; - case 4, - hdr.dime.bitpix = 16; precision = 'int16'; - case 8, - hdr.dime.bitpix = 32; precision = 'int32'; - case 16, - hdr.dime.bitpix = 32; precision = 'float32'; - case 32, - hdr.dime.bitpix = 64; precision = 'float32'; - case 64, - hdr.dime.bitpix = 64; precision = 'float64'; - case 128, - hdr.dime.bitpix = 24; precision = 'uint8'; - case 256 - hdr.dime.bitpix = 8; precision = 'int8'; - case 511 - hdr.dime.bitpix = 96; precision = 'float32'; - case 512 - hdr.dime.bitpix = 16; precision = 'uint16'; - case 768 - hdr.dime.bitpix = 32; precision = 'uint32'; - case 1024 - hdr.dime.bitpix = 64; precision = 'int64'; - case 1280 - hdr.dime.bitpix = 64; precision = 'uint64'; - case 1792, - hdr.dime.bitpix = 128; precision = 'float64'; - otherwise - error('This datatype is not supported'); - end - - hdr.dime.dim(find(hdr.dime.dim < 1)) = 1; - - % move pointer to the start of image block - % - switch filetype - case {0, 1} + end + + fid = fopen(fn, 'r', machine); + + if fid < 0 + msg = sprintf('Cannot open file %s.', fn); + error(msg); + end + + % Set bitpix according to datatype + % + % /*Acceptable values for datatype are*/ + % + % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN + % 1 Binary (ubit1, bitpix=1) % DT_BINARY + % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 + % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 + % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 + % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 + % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 + % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 + % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 + % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 + % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 + % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 + % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 + % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 + % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 + % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 + % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 + % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 + % + switch hdr.dime.datatype + case 1 + hdr.dime.bitpix = 1; + precision = 'ubit1'; + case 2 + hdr.dime.bitpix = 8; + precision = 'uint8'; + case 4 + hdr.dime.bitpix = 16; + precision = 'int16'; + case 8 + hdr.dime.bitpix = 32; + precision = 'int32'; + case 16 + hdr.dime.bitpix = 32; + precision = 'float32'; + case 32 + hdr.dime.bitpix = 64; + precision = 'float32'; + case 64 + hdr.dime.bitpix = 64; + precision = 'float64'; + case 128 + hdr.dime.bitpix = 24; + precision = 'uint8'; + case 256 + hdr.dime.bitpix = 8; + precision = 'int8'; + case 511 + hdr.dime.bitpix = 96; + precision = 'float32'; + case 512 + hdr.dime.bitpix = 16; + precision = 'uint16'; + case 768 + hdr.dime.bitpix = 32; + precision = 'uint32'; + case 1024 + hdr.dime.bitpix = 64; + precision = 'int64'; + case 1280 + hdr.dime.bitpix = 64; + precision = 'uint64'; + case 1792 + hdr.dime.bitpix = 128; + precision = 'float64'; + otherwise + error('This datatype is not supported'); + end + + hdr.dime.dim(find(hdr.dime.dim < 1)) = 1; + + % move pointer to the start of image block + % + switch filetype + case {0, 1} fseek(fid, 0, 'bof'); - case 2 + case 2 fseek(fid, hdr.dime.vox_offset, 'bof'); - end - - % Load whole image block for old Analyze format or binary image; - % otherwise, load images that are specified in img_idx, dim5_idx, - % dim6_idx, and dim7_idx - % - % For binary image, we have to read all because pos can not be - % seeked in bit and can not be calculated the way below. - % - if hdr.dime.datatype == 1 | isequal(hdr.dime.dim(5:8),ones(1,4)) | ... - (isempty(img_idx) & isempty(dim5_idx) & isempty(dim6_idx) & isempty(dim7_idx)) - - % For each frame, precision of value will be read - % in img_siz times, where img_siz is only the - % dimension size of an image, not the byte storage - % size of an image. - % - img_siz = prod(hdr.dime.dim(2:8)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - - img = fread(fid, img_siz, sprintf('*%s',precision)); - - d1 = hdr.dime.dim(2); - d2 = hdr.dime.dim(3); - d3 = hdr.dime.dim(4); - d4 = hdr.dime.dim(5); - d5 = hdr.dime.dim(6); - d6 = hdr.dime.dim(7); - d7 = hdr.dime.dim(8); - - if isempty(img_idx) - img_idx = 1:d4; - end - - if isempty(dim5_idx) - dim5_idx = 1:d5; - end - - if isempty(dim6_idx) - dim6_idx = 1:d6; - end - - if isempty(dim7_idx) - dim7_idx = 1:d7; - end - else - - d1 = hdr.dime.dim(2); - d2 = hdr.dime.dim(3); - d3 = hdr.dime.dim(4); - d4 = hdr.dime.dim(5); - d5 = hdr.dime.dim(6); - d6 = hdr.dime.dim(7); - d7 = hdr.dime.dim(8); - - if isempty(img_idx) - img_idx = 1:d4; - end - - if isempty(dim5_idx) - dim5_idx = 1:d5; - end - - if isempty(dim6_idx) - dim6_idx = 1:d6; - end - - if isempty(dim7_idx) - dim7_idx = 1:d7; - end - - % compute size of one image - % - img_siz = prod(hdr.dime.dim(2:4)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end + end + + % Load whole image block for old Analyze format or binary image; + % otherwise, load images that are specified in img_idx, dim5_idx, + % dim6_idx, and dim7_idx + % + % For binary image, we have to read all because pos can not be + % seeked in bit and can not be calculated the way below. + % + if hdr.dime.datatype == 1 | isequal(hdr.dime.dim(5:8), ones(1, 4)) | ... + (isempty(img_idx) & isempty(dim5_idx) & isempty(dim6_idx) & isempty(dim7_idx)) + + % For each frame, precision of value will be read + % in img_siz times, where img_siz is only the + % dimension size of an image, not the byte storage + % size of an image. + % + img_siz = prod(hdr.dime.dim(2:8)); + + % For complex float32 or complex float64, voxel values + % include [real, imag] + % + if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 + img_siz = img_siz * 2; + end + + % MPH: For RGB24, voxel values include 3 separate color planes + % + if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 + img_siz = img_siz * 3; + end + + img = fread(fid, img_siz, sprintf('*%s', precision)); + + d1 = hdr.dime.dim(2); + d2 = hdr.dime.dim(3); + d3 = hdr.dime.dim(4); + d4 = hdr.dime.dim(5); + d5 = hdr.dime.dim(6); + d6 = hdr.dime.dim(7); + d7 = hdr.dime.dim(8); + + if isempty(img_idx) + img_idx = 1:d4; + end + + if isempty(dim5_idx) + dim5_idx = 1:d5; + end + + if isempty(dim6_idx) + dim6_idx = 1:d6; + end + + if isempty(dim7_idx) + dim7_idx = 1:d7; + end + else + + d1 = hdr.dime.dim(2); + d2 = hdr.dime.dim(3); + d3 = hdr.dime.dim(4); + d4 = hdr.dime.dim(5); + d5 = hdr.dime.dim(6); + d6 = hdr.dime.dim(7); + d7 = hdr.dime.dim(8); + + if isempty(img_idx) + img_idx = 1:d4; + end + + if isempty(dim5_idx) + dim5_idx = 1:d5; + end + + if isempty(dim6_idx) + dim6_idx = 1:d6; + end + + if isempty(dim7_idx) + dim7_idx = 1:d7; + end + + % compute size of one image + % + img_siz = prod(hdr.dime.dim(2:4)); + + % For complex float32 or complex float64, voxel values + % include [real, imag] + % + if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 + img_siz = img_siz * 2; + end + + % MPH: For RGB24, voxel values include 3 separate color planes + % + if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 + img_siz = img_siz * 3; + end + + % preallocate img + img = zeros(img_siz, length(img_idx) * length(dim5_idx) * length(dim6_idx) * length(dim7_idx)); + currentIndex = 1; + + for i7 = 1:length(dim7_idx) + for i6 = 1:length(dim6_idx) + for i5 = 1:length(dim5_idx) + for t = 1:length(img_idx) + + % Position is seeked in bytes. To convert dimension size + % to byte storage size, hdr.dime.bitpix/8 will be + % applied. + % + pos = sub2ind([d1 d2 d3 d4 d5 d6 d7], 1, 1, 1, ... + img_idx(t), dim5_idx(i5), dim6_idx(i6), dim7_idx(i7)) - 1; + pos = pos * hdr.dime.bitpix / 8; + + if filetype == 2 + fseek(fid, pos + hdr.dime.vox_offset, 'bof'); + else + fseek(fid, pos, 'bof'); + end - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end + % For each frame, fread will read precision of value + % in img_siz times + % + img(:, currentIndex) = fread(fid, img_siz, sprintf('*%s', precision)); + currentIndex = currentIndex + 1; - % preallocate img - img = zeros(img_siz, length(img_idx)*length(dim5_idx)*length(dim6_idx)*length(dim7_idx) ); - currentIndex = 1; - - for i7=1:length(dim7_idx) - for i6=1:length(dim6_idx) - for i5=1:length(dim5_idx) - for t=1:length(img_idx) - - % Position is seeked in bytes. To convert dimension size - % to byte storage size, hdr.dime.bitpix/8 will be - % applied. - % - pos = sub2ind([d1 d2 d3 d4 d5 d6 d7], 1, 1, 1, ... - img_idx(t), dim5_idx(i5),dim6_idx(i6),dim7_idx(i7)) -1; - pos = pos * hdr.dime.bitpix/8; - - if filetype == 2 - fseek(fid, pos + hdr.dime.vox_offset, 'bof'); - else - fseek(fid, pos, 'bof'); - end - - % For each frame, fread will read precision of value - % in img_siz times - % - img(:,currentIndex) = fread(fid, img_siz, sprintf('*%s',precision)); - currentIndex = currentIndex +1; - - end - end - end + end + end end - end - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img = reshape(img, [2, length(img)/2]); - img = complex(img(1,:)', img(2,:)'); - end - - fclose(fid); - - % Update the global min and max values - % - hdr.dime.glmax = double(max(img(:))); - hdr.dime.glmin = double(min(img(:))); - - % old_RGB treat RGB slice by slice, now it is treated voxel by voxel - % - if old_RGB & hdr.dime.datatype == 128 & hdr.dime.bitpix == 24 - % remove squeeze - img = (reshape(img, [hdr.dime.dim(2:3) 3 hdr.dime.dim(4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [1 2 4 3 5 6 7 8]); - elseif hdr.dime.datatype == 128 & hdr.dime.bitpix == 24 - % remove squeeze - img = (reshape(img, [3 hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [2 3 4 1 5 6 7 8]); - elseif hdr.dime.datatype == 511 & hdr.dime.bitpix == 96 - img = double(img(:)); - img = single((img - min(img))/(max(img) - min(img))); - % remove squeeze - img = (reshape(img, [3 hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [2 3 4 1 5 6 7 8]); - else - % remove squeeze - img = (reshape(img, [hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - end - - if ~isempty(img_idx) - hdr.dime.dim(5) = length(img_idx); - end - - if ~isempty(dim5_idx) - hdr.dime.dim(6) = length(dim5_idx); - end - - if ~isempty(dim6_idx) - hdr.dime.dim(7) = length(dim6_idx); - end - - if ~isempty(dim7_idx) - hdr.dime.dim(8) = length(dim7_idx); - end - - return % read_image - + end + end + + % For complex float32 or complex float64, voxel values + % include [real, imag] + % + if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 + img = reshape(img, [2, length(img) / 2]); + img = complex(img(1, :)', img(2, :)'); + end + + fclose(fid); + + % Update the global min and max values + % + hdr.dime.glmax = double(max(img(:))); + hdr.dime.glmin = double(min(img(:))); + + % old_RGB treat RGB slice by slice, now it is treated voxel by voxel + % + if old_RGB & hdr.dime.datatype == 128 & hdr.dime.bitpix == 24 + % remove squeeze + img = (reshape(img, [hdr.dime.dim(2:3) 3 hdr.dime.dim(4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); + img = permute(img, [1 2 4 3 5 6 7 8]); + elseif hdr.dime.datatype == 128 & hdr.dime.bitpix == 24 + % remove squeeze + img = (reshape(img, [3 hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); + img = permute(img, [2 3 4 1 5 6 7 8]); + elseif hdr.dime.datatype == 511 & hdr.dime.bitpix == 96 + img = double(img(:)); + img = single((img - min(img)) / (max(img) - min(img))); + % remove squeeze + img = (reshape(img, [3 hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); + img = permute(img, [2 3 4 1 5 6 7 8]); + else + % remove squeeze + img = (reshape(img, [hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); + end + + if ~isempty(img_idx) + hdr.dime.dim(5) = length(img_idx); + end + + if ~isempty(dim5_idx) + hdr.dime.dim(6) = length(dim5_idx); + end + + if ~isempty(dim6_idx) + hdr.dime.dim(7) = length(dim6_idx); + end + + if ~isempty(dim7_idx) + hdr.dime.dim(8) = length(dim7_idx); + end + + return % read_image diff --git a/lib/NiftiTools/load_untouch0_nii_hdr.m b/lib/NiftiTools/load_untouch0_nii_hdr.m index d780e1de..df8900ae 100644 --- a/lib/NiftiTools/load_untouch0_nii_hdr.m +++ b/lib/NiftiTools/load_untouch0_nii_hdr.m @@ -4,197 +4,192 @@ function hdr = load_untouch0_nii_hdr(fileprefix, machine) - fn = sprintf('%s.hdr',fileprefix); - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); - hdr = read_header(fid); - fclose(fid); - end - - return % load_nii_hdr - - -%--------------------------------------------------------------------- -function [ dsr ] = read_header(fid) - - % Original header structures - % struct dsr - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ - - dsr.hk = header_key(fid); - dsr.dime = image_dimension(fid); - dsr.hist = data_history(fid); - - return % read_header - - -%--------------------------------------------------------------------- -function [ hk ] = header_key(fid) - - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - % - % int sizeof_header Should be 348. - % char regular Must be 'r' to indicate that all images and - % volumes are the same size. - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end - - hk.sizeof_hdr = fread(fid, 1,'int32')'; % should be 348! - hk.data_type = deblank(fread(fid,10,directchar)'); - hk.db_name = deblank(fread(fid,18,directchar)'); - hk.extents = fread(fid, 1,'int32')'; - hk.session_error = fread(fid, 1,'int16')'; - hk.regular = fread(fid, 1,directchar)'; - hk.hkey_un0 = fread(fid, 1,directchar)'; - - return % header_key - - -%--------------------------------------------------------------------- -function [ dime ] = image_dimension(fid) - - %struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % /* - % dim[0] Number of dimensions in database; usually 4. - % dim[1] Image X dimension; number of *pixels* in an image row. - % dim[2] Image Y dimension; number of *pixel rows* in slice. - % dim[3] Volume Z dimension; number of *slices* in a volume. - % dim[4] Time points; number of volumes in database - % */ - % char vox_units[4]; /* 16 + 4 */ - % char cal_units[8]; /* 20 + 8 */ - % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width, mm - % pixdim[2] - voxel height, mm - % pixdim[3] - slice thickness, mm - % pixdim[4] - volume timing, in msec - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float roi_scale; /* 72 + 4 */ - % float funused1; /* 76 + 4 */ - % float funused2; /* 80 + 4 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % int compressed; /* 92 + 4 */ - % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end - - dime.dim = fread(fid,8,'int16')'; - dime.vox_units = deblank(fread(fid,4,directchar)'); - dime.cal_units = deblank(fread(fid,8,directchar)'); - dime.unused1 = fread(fid,1,'int16')'; - dime.datatype = fread(fid,1,'int16')'; - dime.bitpix = fread(fid,1,'int16')'; - dime.dim_un0 = fread(fid,1,'int16')'; - dime.pixdim = fread(fid,8,'float32')'; - dime.vox_offset = fread(fid,1,'float32')'; - dime.roi_scale = fread(fid,1,'float32')'; - dime.funused1 = fread(fid,1,'float32')'; - dime.funused2 = fread(fid,1,'float32')'; - dime.cal_max = fread(fid,1,'float32')'; - dime.cal_min = fread(fid,1,'float32')'; - dime.compressed = fread(fid,1,'int32')'; - dime.verified = fread(fid,1,'int32')'; - dime.glmax = fread(fid,1,'int32')'; - dime.glmin = fread(fid,1,'int32')'; - - return % image_dimension - - -%--------------------------------------------------------------------- -function [ hist ] = data_history(fid) - - %struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % char orient; /* 104 + 1 */ - % char originator[10]; /* 105 + 10 */ - % char generated[10]; /* 115 + 10 */ - % char scannum[10]; /* 125 + 10 */ - % char patient_id[10]; /* 135 + 10 */ - % char exp_date[10]; /* 145 + 10 */ - % char exp_time[10]; /* 155 + 10 */ - % char hist_un0[3]; /* 165 + 3 */ - % int views /* 168 + 4 */ - % int vols_added; /* 172 + 4 */ - % int start_field; /* 176 + 4 */ - % int field_skip; /* 180 + 4 */ - % int omax; /* 184 + 4 */ - % int omin; /* 188 + 4 */ - % int smax; /* 192 + 4 */ - % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end - - hist.descrip = deblank(fread(fid,80,directchar)'); - hist.aux_file = deblank(fread(fid,24,directchar)'); - hist.orient = fread(fid, 1,'char')'; - hist.originator = fread(fid, 5,'int16')'; - hist.generated = deblank(fread(fid,10,directchar)'); - hist.scannum = deblank(fread(fid,10,directchar)'); - hist.patient_id = deblank(fread(fid,10,directchar)'); - hist.exp_date = deblank(fread(fid,10,directchar)'); - hist.exp_time = deblank(fread(fid,10,directchar)'); - hist.hist_un0 = deblank(fread(fid, 3,directchar)'); - hist.views = fread(fid, 1,'int32')'; - hist.vols_added = fread(fid, 1,'int32')'; - hist.start_field = fread(fid, 1,'int32')'; - hist.field_skip = fread(fid, 1,'int32')'; - hist.omax = fread(fid, 1,'int32')'; - hist.omin = fread(fid, 1,'int32')'; - hist.smax = fread(fid, 1,'int32')'; - hist.smin = fread(fid, 1,'int32')'; - - return % data_history - + fn = sprintf('%s.hdr', fileprefix); + fid = fopen(fn, 'r', machine); + + if fid < 0 + msg = sprintf('Cannot open file %s.', fn); + error(msg); + else + fseek(fid, 0, 'bof'); + hdr = read_header(fid); + fclose(fid); + end + + return % load_nii_hdr + + % --------------------------------------------------------------------- +function [dsr] = read_header(fid) + + % Original header structures + % struct dsr + % { + % struct header_key hk; /* 0 + 40 */ + % struct image_dimension dime; /* 40 + 108 */ + % struct data_history hist; /* 148 + 200 */ + % }; /* total= 348 bytes*/ + + dsr.hk = header_key(fid); + dsr.dime = image_dimension(fid); + dsr.hist = data_history(fid); + + return % read_header + + % --------------------------------------------------------------------- +function [hk] = header_key(fid) + + fseek(fid, 0, 'bof'); + + % Original header structures + % struct header_key /* header key */ + % { /* off + size */ + % int sizeof_hdr /* 0 + 4 */ + % char data_type[10]; /* 4 + 10 */ + % char db_name[18]; /* 14 + 18 */ + % int extents; /* 32 + 4 */ + % short int session_error; /* 36 + 2 */ + % char regular; /* 38 + 1 */ + % char hkey_un0; /* 39 + 1 */ + % }; /* total=40 bytes */ + % + % int sizeof_header Should be 348. + % char regular Must be 'r' to indicate that all images and + % volumes are the same size. + + v6 = version; + if str2num(v6(1)) < 6 + directchar = '*char'; + else + directchar = 'uchar=>char'; + end + + hk.sizeof_hdr = fread(fid, 1, 'int32')'; % should be 348! + hk.data_type = deblank(fread(fid, 10, directchar)'); + hk.db_name = deblank(fread(fid, 18, directchar)'); + hk.extents = fread(fid, 1, 'int32')'; + hk.session_error = fread(fid, 1, 'int16')'; + hk.regular = fread(fid, 1, directchar)'; + hk.hkey_un0 = fread(fid, 1, directchar)'; + + return % header_key + + % --------------------------------------------------------------------- +function [dime] = image_dimension(fid) + + % struct image_dimension + % { /* off + size */ + % short int dim[8]; /* 0 + 16 */ + % /* + % dim[0] Number of dimensions in database; usually 4. + % dim[1] Image X dimension; number of *pixels* in an image row. + % dim[2] Image Y dimension; number of *pixel rows* in slice. + % dim[3] Volume Z dimension; number of *slices* in a volume. + % dim[4] Time points; number of volumes in database + % */ + % char vox_units[4]; /* 16 + 4 */ + % char cal_units[8]; /* 20 + 8 */ + % short int unused1; /* 28 + 2 */ + % short int datatype; /* 30 + 2 */ + % short int bitpix; /* 32 + 2 */ + % short int dim_un0; /* 34 + 2 */ + % float pixdim[8]; /* 36 + 32 */ + % /* + % pixdim[] specifies the voxel dimensions: + % pixdim[1] - voxel width, mm + % pixdim[2] - voxel height, mm + % pixdim[3] - slice thickness, mm + % pixdim[4] - volume timing, in msec + % ..etc + % */ + % float vox_offset; /* 68 + 4 */ + % float roi_scale; /* 72 + 4 */ + % float funused1; /* 76 + 4 */ + % float funused2; /* 80 + 4 */ + % float cal_max; /* 84 + 4 */ + % float cal_min; /* 88 + 4 */ + % int compressed; /* 92 + 4 */ + % int verified; /* 96 + 4 */ + % int glmax; /* 100 + 4 */ + % int glmin; /* 104 + 4 */ + % }; /* total=108 bytes */ + + v6 = version; + if str2num(v6(1)) < 6 + directchar = '*char'; + else + directchar = 'uchar=>char'; + end + + dime.dim = fread(fid, 8, 'int16')'; + dime.vox_units = deblank(fread(fid, 4, directchar)'); + dime.cal_units = deblank(fread(fid, 8, directchar)'); + dime.unused1 = fread(fid, 1, 'int16')'; + dime.datatype = fread(fid, 1, 'int16')'; + dime.bitpix = fread(fid, 1, 'int16')'; + dime.dim_un0 = fread(fid, 1, 'int16')'; + dime.pixdim = fread(fid, 8, 'float32')'; + dime.vox_offset = fread(fid, 1, 'float32')'; + dime.roi_scale = fread(fid, 1, 'float32')'; + dime.funused1 = fread(fid, 1, 'float32')'; + dime.funused2 = fread(fid, 1, 'float32')'; + dime.cal_max = fread(fid, 1, 'float32')'; + dime.cal_min = fread(fid, 1, 'float32')'; + dime.compressed = fread(fid, 1, 'int32')'; + dime.verified = fread(fid, 1, 'int32')'; + dime.glmax = fread(fid, 1, 'int32')'; + dime.glmin = fread(fid, 1, 'int32')'; + + return % image_dimension + + % --------------------------------------------------------------------- +function [hist] = data_history(fid) + + % struct data_history + % { /* off + size */ + % char descrip[80]; /* 0 + 80 */ + % char aux_file[24]; /* 80 + 24 */ + % char orient; /* 104 + 1 */ + % char originator[10]; /* 105 + 10 */ + % char generated[10]; /* 115 + 10 */ + % char scannum[10]; /* 125 + 10 */ + % char patient_id[10]; /* 135 + 10 */ + % char exp_date[10]; /* 145 + 10 */ + % char exp_time[10]; /* 155 + 10 */ + % char hist_un0[3]; /* 165 + 3 */ + % int views /* 168 + 4 */ + % int vols_added; /* 172 + 4 */ + % int start_field; /* 176 + 4 */ + % int field_skip; /* 180 + 4 */ + % int omax; /* 184 + 4 */ + % int omin; /* 188 + 4 */ + % int smax; /* 192 + 4 */ + % int smin; /* 196 + 4 */ + % }; /* total=200 bytes */ + + v6 = version; + if str2num(v6(1)) < 6 + directchar = '*char'; + else + directchar = 'uchar=>char'; + end + + hist.descrip = deblank(fread(fid, 80, directchar)'); + hist.aux_file = deblank(fread(fid, 24, directchar)'); + hist.orient = fread(fid, 1, 'char')'; + hist.originator = fread(fid, 5, 'int16')'; + hist.generated = deblank(fread(fid, 10, directchar)'); + hist.scannum = deblank(fread(fid, 10, directchar)'); + hist.patient_id = deblank(fread(fid, 10, directchar)'); + hist.exp_date = deblank(fread(fid, 10, directchar)'); + hist.exp_time = deblank(fread(fid, 10, directchar)'); + hist.hist_un0 = deblank(fread(fid, 3, directchar)'); + hist.views = fread(fid, 1, 'int32')'; + hist.vols_added = fread(fid, 1, 'int32')'; + hist.start_field = fread(fid, 1, 'int32')'; + hist.field_skip = fread(fid, 1, 'int32')'; + hist.omax = fread(fid, 1, 'int32')'; + hist.omin = fread(fid, 1, 'int32')'; + hist.smax = fread(fid, 1, 'int32')'; + hist.smin = fread(fid, 1, 'int32')'; + + return % data_history diff --git a/lib/NiftiTools/load_untouch_header_only.m b/lib/NiftiTools/load_untouch_header_only.m index 8b68084b..b5f0d68a 100644 --- a/lib/NiftiTools/load_untouch_header_only.m +++ b/lib/NiftiTools/load_untouch_header_only.m @@ -3,21 +3,21 @@ % hdr field when using load_untouch_nii to load dataset. Support both % *.nii and *.hdr file extension. If file extension is not provided, % *.hdr will be used as default. -% +% % Usage: [header, ext, filetype, machine] = load_untouch_header_only(filename) -% +% % filename - NIfTI / Analyze file name. -% +% % Returned values: -% +% % header - struct with NIfTI / Analyze header fields. -% +% % ext - NIfTI extension if it is not empty. -% -% filetype - 0 for Analyze format (*.hdr/*.img); -% 1 for NIFTI format in 2 files (*.hdr/*.img); -% 2 for NIFTI format in 1 file (*.nii). -% +% +% filetype - 0 for Analyze format (*.hdr/*.img); +% 1 for NIFTI format in 2 files (*.hdr/*.img); +% 2 for NIFTI format in 1 file (*.nii). +% % machine - a string, see below for details. The default here is 'ieee-le'. % % 'native' or 'n' - local machine format - the default @@ -43,145 +43,156 @@ % function [hdr, ext, filetype, machine] = load_untouch_header_only(filename) - if ~exist('filename','var') - error('Usage: [header, ext, filetype, machine] = load_untouch_header_only(filename)'); - end - - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - % Read the dataset header - % - [hdr, filetype, fileprefix, machine] = load_nii_hdr(filename); - - if filetype == 0 - hdr = load_untouch0_nii_hdr(fileprefix, machine); - ext = []; - else - hdr = load_untouch_nii_hdr(fileprefix, machine, filetype); - - % Read the header extension - % - ext = load_nii_ext(filename); - end - - % Set bitpix according to datatype - % - % /*Acceptable values for datatype are*/ - % - % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN - % 1 Binary (ubit1, bitpix=1) % DT_BINARY - % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 - % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 - % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 - % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 - % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 - % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 - % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 - % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 - % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 - % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 - % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 - % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 - % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 - % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 - % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % - switch hdr.dime.datatype - case 1, - hdr.dime.bitpix = 1; precision = 'ubit1'; - case 2, - hdr.dime.bitpix = 8; precision = 'uint8'; - case 4, - hdr.dime.bitpix = 16; precision = 'int16'; - case 8, - hdr.dime.bitpix = 32; precision = 'int32'; - case 16, - hdr.dime.bitpix = 32; precision = 'float32'; - case 32, - hdr.dime.bitpix = 64; precision = 'float32'; - case 64, - hdr.dime.bitpix = 64; precision = 'float64'; - case 128, - hdr.dime.bitpix = 24; precision = 'uint8'; - case 256 - hdr.dime.bitpix = 8; precision = 'int8'; - case 511 - hdr.dime.bitpix = 96; precision = 'float32'; - case 512 - hdr.dime.bitpix = 16; precision = 'uint16'; - case 768 - hdr.dime.bitpix = 32; precision = 'uint32'; - case 1024 - hdr.dime.bitpix = 64; precision = 'int64'; - case 1280 - hdr.dime.bitpix = 64; precision = 'uint64'; - case 1792, - hdr.dime.bitpix = 128; precision = 'float64'; - otherwise - error('This datatype is not supported'); - end - - tmp = hdr.dime.dim(2:end); - tmp(find(tmp < 1)) = 1; - hdr.dime.dim(2:end) = tmp; - - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - rmdir(tmpDir,'s'); - end - - - return % load_untouch_header_only - + if ~exist('filename', 'var') + error('Usage: [header, ext, filetype, machine] = load_untouch_header_only(filename)'); + end + + v = version; + + % Check file extension. If .gz, unpack it into temp folder + % + if length(filename) > 2 & strcmp(filename(end - 2:end), '.gz') + + if ~strcmp(filename(end - 6:end), '.img.gz') & ... + ~strcmp(filename(end - 6:end), '.hdr.gz') & ... + ~strcmp(filename(end - 6:end), '.nii.gz') + + error('Please check filename.'); + end + + if str2num(v(1:3)) < 7.1 | ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + elseif strcmp(filename(end - 6:end), '.img.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.hdr.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.hdr.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.img.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.nii.gz') + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + filename = gunzip(filename, tmpDir); + filename = char(filename); % convert from cell to string + end + end + + % Read the dataset header + % + [hdr, filetype, fileprefix, machine] = load_nii_hdr(filename); + + if filetype == 0 + hdr = load_untouch0_nii_hdr(fileprefix, machine); + ext = []; + else + hdr = load_untouch_nii_hdr(fileprefix, machine, filetype); + + % Read the header extension + % + ext = load_nii_ext(filename); + end + + % Set bitpix according to datatype + % + % /*Acceptable values for datatype are*/ + % + % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN + % 1 Binary (ubit1, bitpix=1) % DT_BINARY + % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 + % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 + % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 + % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 + % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 + % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 + % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 + % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 + % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 + % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 + % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 + % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 + % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 + % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 + % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 + % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 + % + switch hdr.dime.datatype + case 1 + hdr.dime.bitpix = 1; + precision = 'ubit1'; + case 2 + hdr.dime.bitpix = 8; + precision = 'uint8'; + case 4 + hdr.dime.bitpix = 16; + precision = 'int16'; + case 8 + hdr.dime.bitpix = 32; + precision = 'int32'; + case 16 + hdr.dime.bitpix = 32; + precision = 'float32'; + case 32 + hdr.dime.bitpix = 64; + precision = 'float32'; + case 64 + hdr.dime.bitpix = 64; + precision = 'float64'; + case 128 + hdr.dime.bitpix = 24; + precision = 'uint8'; + case 256 + hdr.dime.bitpix = 8; + precision = 'int8'; + case 511 + hdr.dime.bitpix = 96; + precision = 'float32'; + case 512 + hdr.dime.bitpix = 16; + precision = 'uint16'; + case 768 + hdr.dime.bitpix = 32; + precision = 'uint32'; + case 1024 + hdr.dime.bitpix = 64; + precision = 'int64'; + case 1280 + hdr.dime.bitpix = 64; + precision = 'uint64'; + case 1792 + hdr.dime.bitpix = 128; + precision = 'float64'; + otherwise + error('This datatype is not supported'); + end + + tmp = hdr.dime.dim(2:end); + tmp(find(tmp < 1)) = 1; + hdr.dime.dim(2:end) = tmp; + + % Clean up after gunzip + % + if exist('gzFileName', 'var') + rmdir(tmpDir, 's'); + end + + return % load_untouch_header_only diff --git a/lib/NiftiTools/load_untouch_nii.m b/lib/NiftiTools/load_untouch_nii.m index a2b962bc..dc83fb59 100644 --- a/lib/NiftiTools/load_untouch_nii.m +++ b/lib/NiftiTools/load_untouch_nii.m @@ -14,177 +14,174 @@ % normal situation, you should use "load_nii.m" instead. % % Usage: nii = load_untouch_nii(filename, [img_idx], [dim5_idx], [dim6_idx], ... -% [dim7_idx], [old_RGB], [slice_idx]) +% [dim7_idx], [old_RGB], [slice_idx]) % -% filename - NIFTI or ANALYZE file name. +% filename - NIFTI or ANALYZE file name. % % img_idx (optional) - a numerical array of image volume indices. -% Only the specified volumes will be loaded. All available image -% volumes will be loaded, if it is default or empty. +% Only the specified volumes will be loaded. All available image +% volumes will be loaded, if it is default or empty. % -% The number of images scans can be obtained from get_nii_frame.m, -% or simply: hdr.dime.dim(5). +% The number of images scans can be obtained from get_nii_frame.m, +% or simply: hdr.dime.dim(5). % % dim5_idx (optional) - a numerical array of 5th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. +% Only the specified range will be loaded. All available range +% will be loaded, if it is default or empty. % % dim6_idx (optional) - a numerical array of 6th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. +% Only the specified range will be loaded. All available range +% will be loaded, if it is default or empty. % % dim7_idx (optional) - a numerical array of 7th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. +% Only the specified range will be loaded. All available range +% will be loaded, if it is default or empty. % % old_RGB (optional) - a scale number to tell difference of new RGB24 -% from old RGB24. New RGB24 uses RGB triple sequentially for each -% voxel, like [R1 G1 B1 R2 G2 B2 ...]. Analyze 6.0 from AnalyzeDirect -% uses old RGB24, in a way like [R1 R2 ... G1 G2 ... B1 B2 ...] for -% each slices. If the image that you view is garbled, try to set -% old_RGB variable to 1 and try again, because it could be in -% old RGB24. It will be set to 0, if it is default or empty. +% from old RGB24. New RGB24 uses RGB triple sequentially for each +% voxel, like [R1 G1 B1 R2 G2 B2 ...]. Analyze 6.0 from AnalyzeDirect +% uses old RGB24, in a way like [R1 R2 ... G1 G2 ... B1 B2 ...] for +% each slices. If the image that you view is garbled, try to set +% old_RGB variable to 1 and try again, because it could be in +% old RGB24. It will be set to 0, if it is default or empty. % % slice_idx (optional) - a numerical array of image slice indices. -% Only the specified slices will be loaded. All available image -% slices will be loaded, if it is default or empty. +% Only the specified slices will be loaded. All available image +% slices will be loaded, if it is default or empty. % % Returned values: % % nii structure: % -% hdr - struct with NIFTI header fields. +% hdr - struct with NIFTI header fields. % -% filetype - Analyze format .hdr/.img (0); -% NIFTI .hdr/.img (1); -% NIFTI .nii (2) +% filetype - Analyze format .hdr/.img (0); +% NIFTI .hdr/.img (1); +% NIFTI .nii (2) % -% fileprefix - NIFTI filename without extension. +% fileprefix - NIFTI filename without extension. % -% machine - machine string variable. +% machine - machine string variable. % -% img - 3D (or 4D) matrix of NIFTI data. +% img - 3D (or 4D) matrix of NIFTI data. % % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % function nii = load_untouch_nii(filename, img_idx, dim5_idx, dim6_idx, dim7_idx, ... - old_RGB, slice_idx) - - if ~exist('filename','var') - error('Usage: nii = load_untouch_nii(filename, [img_idx], [dim5_idx], [dim6_idx], [dim7_idx], [old_RGB], [slice_idx])'); - end - - if ~exist('img_idx','var') || isempty(img_idx) - img_idx = []; - end - - if ~exist('dim5_idx','var') || isempty(dim5_idx) - dim5_idx = []; - end - - if ~exist('dim6_idx','var') || isempty(dim6_idx) - dim6_idx = []; - end - - if ~exist('dim7_idx','var') || isempty(dim7_idx) - dim7_idx = []; - end - - if ~exist('old_RGB','var') || isempty(old_RGB) - old_RGB = 0; - end - - if ~exist('slice_idx','var') || isempty(slice_idx) - slice_idx = []; - end - - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 && strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') && ... - ~strcmp(filename(end-6:end), '.hdr.gz') && ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 || ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - % Read the dataset header - % - [nii.hdr,nii.filetype,nii.fileprefix,nii.machine] = load_nii_hdr(filename); - - if nii.filetype == 0 - nii.hdr = load_untouch0_nii_hdr(nii.fileprefix,nii.machine); - nii.ext = []; - else - nii.hdr = load_untouch_nii_hdr(nii.fileprefix,nii.machine,nii.filetype); - - % Read the header extension - % - nii.ext = load_nii_ext(filename); - end - - % Read the dataset body - % - [nii.img,nii.hdr] = load_untouch_nii_img(nii.hdr,nii.filetype,nii.fileprefix, ... - nii.machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB,slice_idx); - - % Perform some of sform/qform transform - % -% nii = xform_nii(nii, tolerance, preferredForm); - - nii.untouch = 1; - - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - - % fix fileprefix so it doesn't point to temp location - % - nii.fileprefix = gzFileName(1:end-7); - rmdir(tmpDir,'s'); - end - - - return % load_untouch_nii + old_RGB, slice_idx) + + if ~exist('filename', 'var') + error('Usage: nii = load_untouch_nii(filename, [img_idx], [dim5_idx], [dim6_idx], [dim7_idx], [old_RGB], [slice_idx])'); + end + + if ~exist('img_idx', 'var') || isempty(img_idx) + img_idx = []; + end + + if ~exist('dim5_idx', 'var') || isempty(dim5_idx) + dim5_idx = []; + end + + if ~exist('dim6_idx', 'var') || isempty(dim6_idx) + dim6_idx = []; + end + + if ~exist('dim7_idx', 'var') || isempty(dim7_idx) + dim7_idx = []; + end + + if ~exist('old_RGB', 'var') || isempty(old_RGB) + old_RGB = 0; + end + + if ~exist('slice_idx', 'var') || isempty(slice_idx) + slice_idx = []; + end + + v = version; + + % Check file extension. If .gz, unpack it into temp folder + % + if length(filename) > 2 && strcmp(filename(end - 2:end), '.gz') + + if ~strcmp(filename(end - 6:end), '.img.gz') && ... + ~strcmp(filename(end - 6:end), '.hdr.gz') && ... + ~strcmp(filename(end - 6:end), '.nii.gz') + + error('Please check filename.'); + end + + if str2num(v(1:3)) < 7.1 || ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + elseif strcmp(filename(end - 6:end), '.img.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.hdr.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.hdr.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.img.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.nii.gz') + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + filename = gunzip(filename, tmpDir); + filename = char(filename); % convert from cell to string + end + end + + % Read the dataset header + % + [nii.hdr, nii.filetype, nii.fileprefix, nii.machine] = load_nii_hdr(filename); + + if nii.filetype == 0 + nii.hdr = load_untouch0_nii_hdr(nii.fileprefix, nii.machine); + nii.ext = []; + else + nii.hdr = load_untouch_nii_hdr(nii.fileprefix, nii.machine, nii.filetype); + + % Read the header extension + % + nii.ext = load_nii_ext(filename); + end + + % Read the dataset body + % + [nii.img, nii.hdr] = load_untouch_nii_img(nii.hdr, nii.filetype, nii.fileprefix, ... + nii.machine, img_idx, dim5_idx, dim6_idx, dim7_idx, old_RGB, slice_idx); + + % Perform some of sform/qform transform + % + % nii = xform_nii(nii, tolerance, preferredForm); + + nii.untouch = 1; + + % Clean up after gunzip + % + if exist('gzFileName', 'var') + + % fix fileprefix so it doesn't point to temp location + % + nii.fileprefix = gzFileName(1:end - 7); + rmdir(tmpDir, 's'); + end + + return % load_untouch_nii diff --git a/lib/NiftiTools/load_untouch_nii_hdr.m b/lib/NiftiTools/load_untouch_nii_hdr.m index c88e3987..278be3e5 100644 --- a/lib/NiftiTools/load_untouch_nii_hdr.m +++ b/lib/NiftiTools/load_untouch_nii_hdr.m @@ -4,213 +4,209 @@ function hdr = load_untouch_nii_hdr(fileprefix, machine, filetype) - if filetype == 2 - fn = sprintf('%s.nii',fileprefix); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.nii".', fileprefix); - error(msg); - end - else - fn = sprintf('%s.hdr',fileprefix); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.hdr".', fileprefix); - error(msg); - end - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); - hdr = read_header(fid); - fclose(fid); - end - - return % load_nii_hdr - - -%--------------------------------------------------------------------- -function [ dsr ] = read_header(fid) - - % Original header structures - % struct dsr - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ - - dsr.hk = header_key(fid); - dsr.dime = image_dimension(fid); - dsr.hist = data_history(fid); - - % For Analyze data format - % - if ~strcmp(dsr.hist.magic, 'n+1') && ~strcmp(dsr.hist.magic, 'ni1') - dsr.hist.qform_code = 0; - dsr.hist.sform_code = 0; - end + if filetype == 2 + fn = sprintf('%s.nii', fileprefix); - return % read_header - - -%--------------------------------------------------------------------- -function [ hk ] = header_key(fid) - - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char dim_info; % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - % - % int sizeof_header Should be 348. - % char regular Must be 'r' to indicate that all images and - % volumes are the same size. - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; + if ~exist(fn) + msg = sprintf('Cannot find file "%s.nii".', fileprefix); + error(msg); end + else + fn = sprintf('%s.hdr', fileprefix); - hk.sizeof_hdr = fread(fid, 1,'int32')'; % should be 348! - hk.data_type = deblank(fread(fid,10,directchar)'); - hk.db_name = deblank(fread(fid,18,directchar)'); - hk.extents = fread(fid, 1,'int32')'; - hk.session_error = fread(fid, 1,'int16')'; - hk.regular = fread(fid, 1,directchar)'; - hk.dim_info = fread(fid, 1,'uchar')'; - - return % header_key - - -%--------------------------------------------------------------------- -function [ dime ] = image_dimension(fid) - - % Original header structures - % struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % /* - % dim[0] Number of dimensions in database; usually 4. - % dim[1] Image X dimension; number of *pixels* in an image row. - % dim[2] Image Y dimension; number of *pixel rows* in slice. - % dim[3] Volume Z dimension; number of *slices* in a volume. - % dim[4] Time points; number of volumes in database - % */ - % float intent_p1; % char vox_units[4]; /* 16 + 4 */ - % float intent_p2; % char cal_units[8]; /* 20 + 4 */ - % float intent_p3; % char cal_units[8]; /* 24 + 4 */ - % short int intent_code; % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int slice_start; % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width, mm - % pixdim[2] - voxel height, mm - % pixdim[3] - slice thickness, mm - % pixdim[4] - volume timing, in msec - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float scl_slope; % float roi_scale; /* 72 + 4 */ - % float scl_inter; % float funused1; /* 76 + 4 */ - % short slice_end; % float funused2; /* 80 + 2 */ - % char slice_code; % float funused2; /* 82 + 1 */ - % char xyzt_units; % float funused2; /* 83 + 1 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % float slice_duration; % int compressed; /* 92 + 4 */ - % float toffset; % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - dime.dim = fread(fid,8,'int16')'; - dime.intent_p1 = fread(fid,1,'float32')'; - dime.intent_p2 = fread(fid,1,'float32')'; - dime.intent_p3 = fread(fid,1,'float32')'; - dime.intent_code = fread(fid,1,'int16')'; - dime.datatype = fread(fid,1,'int16')'; - dime.bitpix = fread(fid,1,'int16')'; - dime.slice_start = fread(fid,1,'int16')'; - dime.pixdim = fread(fid,8,'float32')'; - dime.vox_offset = fread(fid,1,'float32')'; - dime.scl_slope = fread(fid,1,'float32')'; - dime.scl_inter = fread(fid,1,'float32')'; - dime.slice_end = fread(fid,1,'int16')'; - dime.slice_code = fread(fid,1,'uchar')'; - dime.xyzt_units = fread(fid,1,'uchar')'; - dime.cal_max = fread(fid,1,'float32')'; - dime.cal_min = fread(fid,1,'float32')'; - dime.slice_duration = fread(fid,1,'float32')'; - dime.toffset = fread(fid,1,'float32')'; - dime.glmax = fread(fid,1,'int32')'; - dime.glmin = fread(fid,1,'int32')'; - - return % image_dimension - - -%--------------------------------------------------------------------- -function [ hist ] = data_history(fid) - - % Original header structures - % struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % short int qform_code; /* 104 + 2 */ - % short int sform_code; /* 106 + 2 */ - % float quatern_b; /* 108 + 4 */ - % float quatern_c; /* 112 + 4 */ - % float quatern_d; /* 116 + 4 */ - % float qoffset_x; /* 120 + 4 */ - % float qoffset_y; /* 124 + 4 */ - % float qoffset_z; /* 128 + 4 */ - % float srow_x[4]; /* 132 + 16 */ - % float srow_y[4]; /* 148 + 16 */ - % float srow_z[4]; /* 164 + 16 */ - % char intent_name[16]; /* 180 + 16 */ - % char magic[4]; % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; + if ~exist(fn) + msg = sprintf('Cannot find file "%s.hdr".', fileprefix); + error(msg); end - - hist.descrip = deblank(fread(fid,80,directchar)'); - hist.aux_file = deblank(fread(fid,24,directchar)'); - hist.qform_code = fread(fid,1,'int16')'; - hist.sform_code = fread(fid,1,'int16')'; - hist.quatern_b = fread(fid,1,'float32')'; - hist.quatern_c = fread(fid,1,'float32')'; - hist.quatern_d = fread(fid,1,'float32')'; - hist.qoffset_x = fread(fid,1,'float32')'; - hist.qoffset_y = fread(fid,1,'float32')'; - hist.qoffset_z = fread(fid,1,'float32')'; - hist.srow_x = fread(fid,4,'float32')'; - hist.srow_y = fread(fid,4,'float32')'; - hist.srow_z = fread(fid,4,'float32')'; - hist.intent_name = deblank(fread(fid,16,directchar)'); - hist.magic = deblank(fread(fid,4,directchar)'); - - return % data_history + end + + fid = fopen(fn, 'r', machine); + + if fid < 0 + msg = sprintf('Cannot open file %s.', fn); + error(msg); + else + fseek(fid, 0, 'bof'); + hdr = read_header(fid); + fclose(fid); + end + + return % load_nii_hdr + + % --------------------------------------------------------------------- +function [dsr] = read_header(fid) + + % Original header structures + % struct dsr + % { + % struct header_key hk; /* 0 + 40 */ + % struct image_dimension dime; /* 40 + 108 */ + % struct data_history hist; /* 148 + 200 */ + % }; /* total= 348 bytes*/ + + dsr.hk = header_key(fid); + dsr.dime = image_dimension(fid); + dsr.hist = data_history(fid); + + % For Analyze data format + % + if ~strcmp(dsr.hist.magic, 'n+1') && ~strcmp(dsr.hist.magic, 'ni1') + dsr.hist.qform_code = 0; + dsr.hist.sform_code = 0; + end + + return % read_header + + % --------------------------------------------------------------------- +function [hk] = header_key(fid) + + fseek(fid, 0, 'bof'); + + % Original header structures + % struct header_key /* header key */ + % { /* off + size */ + % int sizeof_hdr /* 0 + 4 */ + % char data_type[10]; /* 4 + 10 */ + % char db_name[18]; /* 14 + 18 */ + % int extents; /* 32 + 4 */ + % short int session_error; /* 36 + 2 */ + % char regular; /* 38 + 1 */ + % char dim_info; % char hkey_un0; /* 39 + 1 */ + % }; /* total=40 bytes */ + % + % int sizeof_header Should be 348. + % char regular Must be 'r' to indicate that all images and + % volumes are the same size. + + v6 = version; + if str2num(v6(1)) < 6 + directchar = '*char'; + else + directchar = 'uchar=>char'; + end + + hk.sizeof_hdr = fread(fid, 1, 'int32')'; % should be 348! + hk.data_type = deblank(fread(fid, 10, directchar)'); + hk.db_name = deblank(fread(fid, 18, directchar)'); + hk.extents = fread(fid, 1, 'int32')'; + hk.session_error = fread(fid, 1, 'int16')'; + hk.regular = fread(fid, 1, directchar)'; + hk.dim_info = fread(fid, 1, 'uchar')'; + + return % header_key + + % --------------------------------------------------------------------- +function [dime] = image_dimension(fid) + + % Original header structures + % struct image_dimension + % { /* off + size */ + % short int dim[8]; /* 0 + 16 */ + % /* + % dim[0] Number of dimensions in database; usually 4. + % dim[1] Image X dimension; number of *pixels* in an image row. + % dim[2] Image Y dimension; number of *pixel rows* in slice. + % dim[3] Volume Z dimension; number of *slices* in a volume. + % dim[4] Time points; number of volumes in database + % */ + % float intent_p1; % char vox_units[4]; /* 16 + 4 */ + % float intent_p2; % char cal_units[8]; /* 20 + 4 */ + % float intent_p3; % char cal_units[8]; /* 24 + 4 */ + % short int intent_code; % short int unused1; /* 28 + 2 */ + % short int datatype; /* 30 + 2 */ + % short int bitpix; /* 32 + 2 */ + % short int slice_start; % short int dim_un0; /* 34 + 2 */ + % float pixdim[8]; /* 36 + 32 */ + % /* + % pixdim[] specifies the voxel dimensions: + % pixdim[1] - voxel width, mm + % pixdim[2] - voxel height, mm + % pixdim[3] - slice thickness, mm + % pixdim[4] - volume timing, in msec + % ..etc + % */ + % float vox_offset; /* 68 + 4 */ + % float scl_slope; % float roi_scale; /* 72 + 4 */ + % float scl_inter; % float funused1; /* 76 + 4 */ + % short slice_end; % float funused2; /* 80 + 2 */ + % char slice_code; % float funused2; /* 82 + 1 */ + % char xyzt_units; % float funused2; /* 83 + 1 */ + % float cal_max; /* 84 + 4 */ + % float cal_min; /* 88 + 4 */ + % float slice_duration; % int compressed; /* 92 + 4 */ + % float toffset; % int verified; /* 96 + 4 */ + % int glmax; /* 100 + 4 */ + % int glmin; /* 104 + 4 */ + % }; /* total=108 bytes */ + + dime.dim = fread(fid, 8, 'int16')'; + dime.intent_p1 = fread(fid, 1, 'float32')'; + dime.intent_p2 = fread(fid, 1, 'float32')'; + dime.intent_p3 = fread(fid, 1, 'float32')'; + dime.intent_code = fread(fid, 1, 'int16')'; + dime.datatype = fread(fid, 1, 'int16')'; + dime.bitpix = fread(fid, 1, 'int16')'; + dime.slice_start = fread(fid, 1, 'int16')'; + dime.pixdim = fread(fid, 8, 'float32')'; + dime.vox_offset = fread(fid, 1, 'float32')'; + dime.scl_slope = fread(fid, 1, 'float32')'; + dime.scl_inter = fread(fid, 1, 'float32')'; + dime.slice_end = fread(fid, 1, 'int16')'; + dime.slice_code = fread(fid, 1, 'uchar')'; + dime.xyzt_units = fread(fid, 1, 'uchar')'; + dime.cal_max = fread(fid, 1, 'float32')'; + dime.cal_min = fread(fid, 1, 'float32')'; + dime.slice_duration = fread(fid, 1, 'float32')'; + dime.toffset = fread(fid, 1, 'float32')'; + dime.glmax = fread(fid, 1, 'int32')'; + dime.glmin = fread(fid, 1, 'int32')'; + + return % image_dimension + + % --------------------------------------------------------------------- +function [hist] = data_history(fid) + + % Original header structures + % struct data_history + % { /* off + size */ + % char descrip[80]; /* 0 + 80 */ + % char aux_file[24]; /* 80 + 24 */ + % short int qform_code; /* 104 + 2 */ + % short int sform_code; /* 106 + 2 */ + % float quatern_b; /* 108 + 4 */ + % float quatern_c; /* 112 + 4 */ + % float quatern_d; /* 116 + 4 */ + % float qoffset_x; /* 120 + 4 */ + % float qoffset_y; /* 124 + 4 */ + % float qoffset_z; /* 128 + 4 */ + % float srow_x[4]; /* 132 + 16 */ + % float srow_y[4]; /* 148 + 16 */ + % float srow_z[4]; /* 164 + 16 */ + % char intent_name[16]; /* 180 + 16 */ + % char magic[4]; % int smin; /* 196 + 4 */ + % }; /* total=200 bytes */ + + v6 = version; + if str2num(v6(1)) < 6 + directchar = '*char'; + else + directchar = 'uchar=>char'; + end + + hist.descrip = deblank(fread(fid, 80, directchar)'); + hist.aux_file = deblank(fread(fid, 24, directchar)'); + hist.qform_code = fread(fid, 1, 'int16')'; + hist.sform_code = fread(fid, 1, 'int16')'; + hist.quatern_b = fread(fid, 1, 'float32')'; + hist.quatern_c = fread(fid, 1, 'float32')'; + hist.quatern_d = fread(fid, 1, 'float32')'; + hist.qoffset_x = fread(fid, 1, 'float32')'; + hist.qoffset_y = fread(fid, 1, 'float32')'; + hist.qoffset_z = fread(fid, 1, 'float32')'; + hist.srow_x = fread(fid, 4, 'float32')'; + hist.srow_y = fread(fid, 4, 'float32')'; + hist.srow_z = fread(fid, 4, 'float32')'; + hist.intent_name = deblank(fread(fid, 16, directchar)'); + hist.magic = deblank(fread(fid, 4, directchar)'); + + return % data_history diff --git a/lib/NiftiTools/load_untouch_nii_img.m b/lib/NiftiTools/load_untouch_nii_img.m index 9fc7fcf6..7c53bbac 100644 --- a/lib/NiftiTools/load_untouch_nii_img.m +++ b/lib/NiftiTools/load_untouch_nii_img.m @@ -2,466 +2,480 @@ % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -function [img,hdr] = load_untouch_nii_img(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB,slice_idx) - - if ~exist('hdr','var') || ~exist('filetype','var') || ~exist('fileprefix','var') || ~exist('machine','var') - error('Usage: [img,hdr] = load_nii_img(hdr,filetype,fileprefix,machine,[img_idx],[dim5_idx],[dim6_idx],[dim7_idx],[old_RGB],[slice_idx]);'); - end - - if ~exist('img_idx','var') || isempty(img_idx) || hdr.dime.dim(5)<1 - img_idx = []; - end - - if ~exist('dim5_idx','var') || isempty(dim5_idx) || hdr.dime.dim(6)<1 - dim5_idx = []; - end - - if ~exist('dim6_idx','var') || isempty(dim6_idx) || hdr.dime.dim(7)<1 - dim6_idx = []; - end - - if ~exist('dim7_idx','var') || isempty(dim7_idx) || hdr.dime.dim(8)<1 - dim7_idx = []; - end - - if ~exist('old_RGB','var') || isempty(old_RGB) - old_RGB = 0; - end - - if ~exist('slice_idx','var') || isempty(slice_idx) || hdr.dime.dim(4)<1 - slice_idx = []; - end - - % check img_idx - % - if ~isempty(img_idx) && ~isnumeric(img_idx) - error('"img_idx" should be a numerical array.'); - end - - if length(unique(img_idx)) ~= length(img_idx) - error('Duplicate image index in "img_idx"'); - end - - if ~isempty(img_idx) && (min(img_idx) < 1 || max(img_idx) > hdr.dime.dim(5)) - max_range = hdr.dime.dim(5); - - if max_range == 1 - error(['"img_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"img_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim5_idx - % - if ~isempty(dim5_idx) && ~isnumeric(dim5_idx) - error('"dim5_idx" should be a numerical array.'); - end - - if length(unique(dim5_idx)) ~= length(dim5_idx) - error('Duplicate index in "dim5_idx"'); - end - - if ~isempty(dim5_idx) && (min(dim5_idx) < 1 || max(dim5_idx) > hdr.dime.dim(6)) - max_range = hdr.dime.dim(6); - - if max_range == 1 - error(['"dim5_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim5_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim6_idx - % - if ~isempty(dim6_idx) && ~isnumeric(dim6_idx) - error('"dim6_idx" should be a numerical array.'); - end - - if length(unique(dim6_idx)) ~= length(dim6_idx) - error('Duplicate index in "dim6_idx"'); - end - - if ~isempty(dim6_idx) && (min(dim6_idx) < 1 || max(dim6_idx) > hdr.dime.dim(7)) - max_range = hdr.dime.dim(7); - - if max_range == 1 - error(['"dim6_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim6_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim7_idx - % - if ~isempty(dim7_idx) && ~isnumeric(dim7_idx) - error('"dim7_idx" should be a numerical array.'); - end - - if length(unique(dim7_idx)) ~= length(dim7_idx) - error('Duplicate index in "dim7_idx"'); - end - - if ~isempty(dim7_idx) && (min(dim7_idx) < 1 || max(dim7_idx) > hdr.dime.dim(8)) - max_range = hdr.dime.dim(8); - - if max_range == 1 - error(['"dim7_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim7_idx" should be an integer within the range of [' range '].']); - end - end - - % check slice_idx - % - if ~isempty(slice_idx) && ~isnumeric(slice_idx) - error('"slice_idx" should be a numerical array.'); - end - - if length(unique(slice_idx)) ~= length(slice_idx) - error('Duplicate index in "slice_idx"'); - end - - if ~isempty(slice_idx) && (min(slice_idx) < 1 || max(slice_idx) > hdr.dime.dim(4)) - max_range = hdr.dime.dim(4); - - if max_range == 1 - error(['"slice_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"slice_idx" should be an integer within the range of [' range '].']); - end - end - - [img,hdr] = read_image(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB,slice_idx); - - return % load_nii_img - - -%--------------------------------------------------------------------- -function [img,hdr] = read_image(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB,slice_idx) - - switch filetype - case {0, 1} +function [img, hdr] = load_untouch_nii_img(hdr, filetype, fileprefix, machine, img_idx, dim5_idx, dim6_idx, dim7_idx, old_RGB, slice_idx) + + if ~exist('hdr', 'var') || ~exist('filetype', 'var') || ~exist('fileprefix', 'var') || ~exist('machine', 'var') + error('Usage: [img,hdr] = load_nii_img(hdr,filetype,fileprefix,machine,[img_idx],[dim5_idx],[dim6_idx],[dim7_idx],[old_RGB],[slice_idx]);'); + end + + if ~exist('img_idx', 'var') || isempty(img_idx) || hdr.dime.dim(5) < 1 + img_idx = []; + end + + if ~exist('dim5_idx', 'var') || isempty(dim5_idx) || hdr.dime.dim(6) < 1 + dim5_idx = []; + end + + if ~exist('dim6_idx', 'var') || isempty(dim6_idx) || hdr.dime.dim(7) < 1 + dim6_idx = []; + end + + if ~exist('dim7_idx', 'var') || isempty(dim7_idx) || hdr.dime.dim(8) < 1 + dim7_idx = []; + end + + if ~exist('old_RGB', 'var') || isempty(old_RGB) + old_RGB = 0; + end + + if ~exist('slice_idx', 'var') || isempty(slice_idx) || hdr.dime.dim(4) < 1 + slice_idx = []; + end + + % check img_idx + % + if ~isempty(img_idx) && ~isnumeric(img_idx) + error('"img_idx" should be a numerical array.'); + end + + if length(unique(img_idx)) ~= length(img_idx) + error('Duplicate image index in "img_idx"'); + end + + if ~isempty(img_idx) && (min(img_idx) < 1 || max(img_idx) > hdr.dime.dim(5)) + max_range = hdr.dime.dim(5); + + if max_range == 1 + error(['"img_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"img_idx" should be an integer within the range of [' range '].']); + end + end + + % check dim5_idx + % + if ~isempty(dim5_idx) && ~isnumeric(dim5_idx) + error('"dim5_idx" should be a numerical array.'); + end + + if length(unique(dim5_idx)) ~= length(dim5_idx) + error('Duplicate index in "dim5_idx"'); + end + + if ~isempty(dim5_idx) && (min(dim5_idx) < 1 || max(dim5_idx) > hdr.dime.dim(6)) + max_range = hdr.dime.dim(6); + + if max_range == 1 + error(['"dim5_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"dim5_idx" should be an integer within the range of [' range '].']); + end + end + + % check dim6_idx + % + if ~isempty(dim6_idx) && ~isnumeric(dim6_idx) + error('"dim6_idx" should be a numerical array.'); + end + + if length(unique(dim6_idx)) ~= length(dim6_idx) + error('Duplicate index in "dim6_idx"'); + end + + if ~isempty(dim6_idx) && (min(dim6_idx) < 1 || max(dim6_idx) > hdr.dime.dim(7)) + max_range = hdr.dime.dim(7); + + if max_range == 1 + error(['"dim6_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"dim6_idx" should be an integer within the range of [' range '].']); + end + end + + % check dim7_idx + % + if ~isempty(dim7_idx) && ~isnumeric(dim7_idx) + error('"dim7_idx" should be a numerical array.'); + end + + if length(unique(dim7_idx)) ~= length(dim7_idx) + error('Duplicate index in "dim7_idx"'); + end + + if ~isempty(dim7_idx) && (min(dim7_idx) < 1 || max(dim7_idx) > hdr.dime.dim(8)) + max_range = hdr.dime.dim(8); + + if max_range == 1 + error(['"dim7_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"dim7_idx" should be an integer within the range of [' range '].']); + end + end + + % check slice_idx + % + if ~isempty(slice_idx) && ~isnumeric(slice_idx) + error('"slice_idx" should be a numerical array.'); + end + + if length(unique(slice_idx)) ~= length(slice_idx) + error('Duplicate index in "slice_idx"'); + end + + if ~isempty(slice_idx) && (min(slice_idx) < 1 || max(slice_idx) > hdr.dime.dim(4)) + max_range = hdr.dime.dim(4); + + if max_range == 1 + error(['"slice_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"slice_idx" should be an integer within the range of [' range '].']); + end + end + + [img, hdr] = read_image(hdr, filetype, fileprefix, machine, img_idx, dim5_idx, dim6_idx, dim7_idx, old_RGB, slice_idx); + + return % load_nii_img + + % --------------------------------------------------------------------- +function [img, hdr] = read_image(hdr, filetype, fileprefix, machine, img_idx, dim5_idx, dim6_idx, dim7_idx, old_RGB, slice_idx) + + switch filetype + case {0, 1} fn = [fileprefix '.img']; - case 2 + case 2 fn = [fileprefix '.nii']; - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - end - - % Set bitpix according to datatype - % - % /*Acceptable values for datatype are*/ - % - % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN - % 1 Binary (ubit1, bitpix=1) % DT_BINARY - % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 - % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 - % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 - % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 - % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 - % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 - % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 - % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 - % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 - % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 - % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 - % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 - % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 - % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 - % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % - switch hdr.dime.datatype - case 1, - hdr.dime.bitpix = 1; precision = 'ubit1'; - case 2, - hdr.dime.bitpix = 8; precision = 'uint8'; - case 4, - hdr.dime.bitpix = 16; precision = 'int16'; - case 8, - hdr.dime.bitpix = 32; precision = 'int32'; - case 16, - hdr.dime.bitpix = 32; precision = 'float32'; - case 32, - hdr.dime.bitpix = 64; precision = 'float32'; - case 64, - hdr.dime.bitpix = 64; precision = 'float64'; - case 128, - hdr.dime.bitpix = 24; precision = 'uint8'; - case 256 - hdr.dime.bitpix = 8; precision = 'int8'; - case 511 - hdr.dime.bitpix = 96; precision = 'float32'; - case 512 - hdr.dime.bitpix = 16; precision = 'uint16'; - case 768 - hdr.dime.bitpix = 32; precision = 'uint32'; - case 1024 - hdr.dime.bitpix = 64; precision = 'int64'; - case 1280 - hdr.dime.bitpix = 64; precision = 'uint64'; - case 1792, - hdr.dime.bitpix = 128; precision = 'float64'; - otherwise + end + + fid = fopen(fn, 'r', machine); + + if fid < 0 + msg = sprintf('Cannot open file %s.', fn); + error(msg); + end + + % Set bitpix according to datatype + % + % /*Acceptable values for datatype are*/ + % + % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN + % 1 Binary (ubit1, bitpix=1) % DT_BINARY + % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 + % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 + % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 + % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 + % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 + % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 + % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 + % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 + % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 + % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 + % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 + % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 + % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 + % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 + % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 + % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 + % + switch hdr.dime.datatype + case 1 + hdr.dime.bitpix = 1; + precision = 'ubit1'; + case 2 + hdr.dime.bitpix = 8; + precision = 'uint8'; + case 4 + hdr.dime.bitpix = 16; + precision = 'int16'; + case 8 + hdr.dime.bitpix = 32; + precision = 'int32'; + case 16 + hdr.dime.bitpix = 32; + precision = 'float32'; + case 32 + hdr.dime.bitpix = 64; + precision = 'float32'; + case 64 + hdr.dime.bitpix = 64; + precision = 'float64'; + case 128 + hdr.dime.bitpix = 24; + precision = 'uint8'; + case 256 + hdr.dime.bitpix = 8; + precision = 'int8'; + case 511 + hdr.dime.bitpix = 96; + precision = 'float32'; + case 512 + hdr.dime.bitpix = 16; + precision = 'uint16'; + case 768 + hdr.dime.bitpix = 32; + precision = 'uint32'; + case 1024 + hdr.dime.bitpix = 64; + precision = 'int64'; + case 1280 + hdr.dime.bitpix = 64; + precision = 'uint64'; + case 1792 + hdr.dime.bitpix = 128; + precision = 'float64'; + otherwise error('This datatype is not supported'); - end + end - tmp = hdr.dime.dim(2:end); - tmp(find(tmp < 1)) = 1; - hdr.dime.dim(2:end) = tmp; + tmp = hdr.dime.dim(2:end); + tmp(find(tmp < 1)) = 1; + hdr.dime.dim(2:end) = tmp; - % move pointer to the start of image block - % - switch filetype - case {0, 1} + % move pointer to the start of image block + % + switch filetype + case {0, 1} fseek(fid, 0, 'bof'); - case 2 + case 2 fseek(fid, hdr.dime.vox_offset, 'bof'); - end - - % Load whole image block for old Analyze format or binary image; - % otherwise, load images that are specified in img_idx, dim5_idx, - % dim6_idx, and dim7_idx - % - % For binary image, we have to read all because pos can not be - % seeked in bit and can not be calculated the way below. - % - if hdr.dime.datatype == 1 || isequal(hdr.dime.dim(4:8),ones(1,5)) || ... - (isempty(img_idx) && isempty(dim5_idx) && isempty(dim6_idx) && isempty(dim7_idx) && isempty(slice_idx)) - - % For each frame, precision of value will be read - % in img_siz times, where img_siz is only the - % dimension size of an image, not the byte storage - % size of an image. + end + + % Load whole image block for old Analyze format or binary image; + % otherwise, load images that are specified in img_idx, dim5_idx, + % dim6_idx, and dim7_idx + % + % For binary image, we have to read all because pos can not be + % seeked in bit and can not be calculated the way below. + % + if hdr.dime.datatype == 1 || isequal(hdr.dime.dim(4:8), ones(1, 5)) || ... + (isempty(img_idx) && isempty(dim5_idx) && isempty(dim6_idx) && isempty(dim7_idx) && isempty(slice_idx)) + + % For each frame, precision of value will be read + % in img_siz times, where img_siz is only the + % dimension size of an image, not the byte storage + % size of an image. + % + img_siz = prod(hdr.dime.dim(2:8)); + + % For complex float32 or complex float64, voxel values + % include [real, imag] + % + if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 + img_siz = img_siz * 2; + end + + % MPH: For RGB24, voxel values include 3 separate color planes + % + if hdr.dime.datatype == 128 || hdr.dime.datatype == 511 + img_siz = img_siz * 3; + end + + img = fread(fid, img_siz, sprintf('*%s', precision)); + + d1 = hdr.dime.dim(2); + d2 = hdr.dime.dim(3); + d3 = hdr.dime.dim(4); + d4 = hdr.dime.dim(5); + d5 = hdr.dime.dim(6); + d6 = hdr.dime.dim(7); + d7 = hdr.dime.dim(8); + + if isempty(slice_idx) + slice_idx = 1:d3; + end + + if isempty(img_idx) + img_idx = 1:d4; + end + + if isempty(dim5_idx) + dim5_idx = 1:d5; + end + + if isempty(dim6_idx) + dim6_idx = 1:d6; + end + + if isempty(dim7_idx) + dim7_idx = 1:d7; + end + else + + d1 = hdr.dime.dim(2); + d2 = hdr.dime.dim(3); + d3 = hdr.dime.dim(4); + d4 = hdr.dime.dim(5); + d5 = hdr.dime.dim(6); + d6 = hdr.dime.dim(7); + d7 = hdr.dime.dim(8); + + if isempty(slice_idx) + slice_idx = 1:d3; + end + + if isempty(img_idx) + img_idx = 1:d4; + end + + if isempty(dim5_idx) + dim5_idx = 1:d5; + end + + if isempty(dim6_idx) + dim6_idx = 1:d6; + end + + if isempty(dim7_idx) + dim7_idx = 1:d7; + end + + % ROMAN: begin + roman = 1; + if roman + + % compute size of one slice % - img_siz = prod(hdr.dime.dim(2:8)); + img_siz = prod(hdr.dime.dim(2:3)); % For complex float32 or complex float64, voxel values % include [real, imag] % if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 - img_siz = img_siz * 2; + img_siz = img_siz * 2; end - %MPH: For RGB24, voxel values include 3 separate color planes + % MPH: For RGB24, voxel values include 3 separate color planes % if hdr.dime.datatype == 128 || hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - - img = fread(fid, img_siz, sprintf('*%s',precision)); - - d1 = hdr.dime.dim(2); - d2 = hdr.dime.dim(3); - d3 = hdr.dime.dim(4); - d4 = hdr.dime.dim(5); - d5 = hdr.dime.dim(6); - d6 = hdr.dime.dim(7); - d7 = hdr.dime.dim(8); - - if isempty(slice_idx) - slice_idx = 1:d3; - end - - if isempty(img_idx) - img_idx = 1:d4; - end - - if isempty(dim5_idx) - dim5_idx = 1:d5; - end - - if isempty(dim6_idx) - dim6_idx = 1:d6; - end - - if isempty(dim7_idx) - dim7_idx = 1:d7; - end - else - - d1 = hdr.dime.dim(2); - d2 = hdr.dime.dim(3); - d3 = hdr.dime.dim(4); - d4 = hdr.dime.dim(5); - d5 = hdr.dime.dim(6); - d6 = hdr.dime.dim(7); - d7 = hdr.dime.dim(8); - - if isempty(slice_idx) - slice_idx = 1:d3; + img_siz = img_siz * 3; end - if isempty(img_idx) - img_idx = 1:d4; - end - - if isempty(dim5_idx) - dim5_idx = 1:d5; - end - - if isempty(dim6_idx) - dim6_idx = 1:d6; - end - - if isempty(dim7_idx) - dim7_idx = 1:d7; - end + % preallocate img + img = zeros(img_siz, length(slice_idx) * length(img_idx) * length(dim5_idx) * length(dim6_idx) * length(dim7_idx)); + currentIndex = 1; + else + img = []; + end % if(roman) + % ROMAN: end + + for i7 = 1:length(dim7_idx) + for i6 = 1:length(dim6_idx) + for i5 = 1:length(dim5_idx) + for t = 1:length(img_idx) + for s = 1:length(slice_idx) + + % Position is seeked in bytes. To convert dimension size + % to byte storage size, hdr.dime.bitpix/8 will be + % applied. + % + pos = sub2ind([d1 d2 d3 d4 d5 d6 d7], 1, 1, slice_idx(s), ... + img_idx(t), dim5_idx(i5), dim6_idx(i6), dim7_idx(i7)) - 1; + pos = pos * hdr.dime.bitpix / 8; + + % ROMAN: begin + if roman + % do nothing + else + img_siz = prod(hdr.dime.dim(2:3)); + + % For complex float32 or complex float64, voxel values + % include [real, imag] + % + if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 + img_siz = img_siz * 2; + end + + % MPH: For RGB24, voxel values include 3 separate color planes + % + if hdr.dime.datatype == 128 || hdr.dime.datatype == 511 + img_siz = img_siz * 3; + end + end % if (roman) + % ROMAN: end + + if filetype == 2 + fseek(fid, pos + hdr.dime.vox_offset, 'bof'); + else + fseek(fid, pos, 'bof'); + end + + % For each frame, fread will read precision of value + % in img_siz times + % + % ROMAN: begin + if roman + img(:, currentIndex) = fread(fid, img_siz, sprintf('*%s', precision)); + currentIndex = currentIndex + 1; + else + img = [img fread(fid, img_siz, sprintf('*%s', precision))]; + end % if(roman) + % ROMAN: end - %ROMAN: begin - roman = 1; - if(roman) - - % compute size of one slice - % - img_siz = prod(hdr.dime.dim(2:3)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 || hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - - % preallocate img - img = zeros(img_siz, length(slice_idx)*length(img_idx)*length(dim5_idx)*length(dim6_idx)*length(dim7_idx) ); - currentIndex = 1; - else - img = []; - end; %if(roman) - % ROMAN: end - - for i7=1:length(dim7_idx) - for i6=1:length(dim6_idx) - for i5=1:length(dim5_idx) - for t=1:length(img_idx) - for s=1:length(slice_idx) - - % Position is seeked in bytes. To convert dimension size - % to byte storage size, hdr.dime.bitpix/8 will be - % applied. - % - pos = sub2ind([d1 d2 d3 d4 d5 d6 d7], 1, 1, slice_idx(s), ... - img_idx(t), dim5_idx(i5),dim6_idx(i6),dim7_idx(i7)) -1; - pos = pos * hdr.dime.bitpix/8; - - % ROMAN: begin - if(roman) - % do nothing - else - img_siz = prod(hdr.dime.dim(2:3)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 || hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - end; % if (roman) - % ROMAN: end - - if filetype == 2 - fseek(fid, pos + hdr.dime.vox_offset, 'bof'); - else - fseek(fid, pos, 'bof'); - end - - % For each frame, fread will read precision of value - % in img_siz times - % - % ROMAN: begin - if(roman) - img(:,currentIndex) = fread(fid, img_siz, sprintf('*%s',precision)); - currentIndex = currentIndex +1; - else - img = [img fread(fid, img_siz, sprintf('*%s',precision))]; - end; %if(roman) - % ROMAN: end - - end - end end - end + end + end end - end - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 - img = reshape(img, [2, length(img)/2]); - img = complex(img(1,:)', img(2,:)'); - end - - fclose(fid); - - % Update the global min and max values - % - hdr.dime.glmax = double(max(img(:))); - hdr.dime.glmin = double(min(img(:))); - - % old_RGB treat RGB slice by slice, now it is treated voxel by voxel - % - if old_RGB && hdr.dime.datatype == 128 && hdr.dime.bitpix == 24 - % remove squeeze - img = (reshape(img, [hdr.dime.dim(2:3) 3 length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [1 2 4 3 5 6 7 8]); - elseif hdr.dime.datatype == 128 && hdr.dime.bitpix == 24 - % remove squeeze - img = (reshape(img, [3 hdr.dime.dim(2:3) length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [2 3 4 1 5 6 7 8]); - elseif hdr.dime.datatype == 511 && hdr.dime.bitpix == 96 - img = double(img(:)); - img = single((img - min(img))/(max(img) - min(img))); - % remove squeeze - img = (reshape(img, [3 hdr.dime.dim(2:3) length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [2 3 4 1 5 6 7 8]); - else - % remove squeeze - img = (reshape(img, [hdr.dime.dim(2:3) length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - end - - if ~isempty(slice_idx) - hdr.dime.dim(4) = length(slice_idx); - end - - if ~isempty(img_idx) - hdr.dime.dim(5) = length(img_idx); - end - - if ~isempty(dim5_idx) - hdr.dime.dim(6) = length(dim5_idx); - end - - if ~isempty(dim6_idx) - hdr.dime.dim(7) = length(dim6_idx); - end - - if ~isempty(dim7_idx) - hdr.dime.dim(8) = length(dim7_idx); - end - - return % read_image + end + end + + % For complex float32 or complex float64, voxel values + % include [real, imag] + % + if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 + img = reshape(img, [2, length(img) / 2]); + img = complex(img(1, :)', img(2, :)'); + end + + fclose(fid); + + % Update the global min and max values + % + hdr.dime.glmax = double(max(img(:))); + hdr.dime.glmin = double(min(img(:))); + + % old_RGB treat RGB slice by slice, now it is treated voxel by voxel + % + if old_RGB && hdr.dime.datatype == 128 && hdr.dime.bitpix == 24 + % remove squeeze + img = (reshape(img, [hdr.dime.dim(2:3) 3 length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); + img = permute(img, [1 2 4 3 5 6 7 8]); + elseif hdr.dime.datatype == 128 && hdr.dime.bitpix == 24 + % remove squeeze + img = (reshape(img, [3 hdr.dime.dim(2:3) length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); + img = permute(img, [2 3 4 1 5 6 7 8]); + elseif hdr.dime.datatype == 511 && hdr.dime.bitpix == 96 + img = double(img(:)); + img = single((img - min(img)) / (max(img) - min(img))); + % remove squeeze + img = (reshape(img, [3 hdr.dime.dim(2:3) length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); + img = permute(img, [2 3 4 1 5 6 7 8]); + else + % remove squeeze + img = (reshape(img, [hdr.dime.dim(2:3) length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); + end + + if ~isempty(slice_idx) + hdr.dime.dim(4) = length(slice_idx); + end + + if ~isempty(img_idx) + hdr.dime.dim(5) = length(img_idx); + end + + if ~isempty(dim5_idx) + hdr.dime.dim(6) = length(dim5_idx); + end + + if ~isempty(dim6_idx) + hdr.dime.dim(7) = length(dim6_idx); + end + + if ~isempty(dim7_idx) + hdr.dime.dim(8) = length(dim7_idx); + end + + return % read_image diff --git a/lib/NiftiTools/make_ana.m b/lib/NiftiTools/make_ana.m index 883fc3ee..c9e14739 100644 --- a/lib/NiftiTools/make_ana.m +++ b/lib/NiftiTools/make_ana.m @@ -1,33 +1,33 @@ % Make ANALYZE 7.5 data structure specified by a 3D or 4D matrix. -% Optional parameters can also be included, such as: voxel_size, -% origin, datatype, and description. -% -% Once the ANALYZE structure is made, it can be saved into ANALYZE 7.5 -% format data file using "save_untouch_nii" command (for more detail, -% type: help save_untouch_nii). -% +% Optional parameters can also be included, such as: voxel_size, +% origin, datatype, and description. +% +% Once the ANALYZE structure is made, it can be saved into ANALYZE 7.5 +% format data file using "save_untouch_nii" command (for more detail, +% type: help save_untouch_nii). +% % Usage: ana = make_ana(img, [voxel_size], [origin], [datatype], [description]) % % Where: % -% img: a 3D matrix [x y z], or a 4D matrix with time -% series [x y z t]. When image is in RGB format, -% make sure that the size of 4th dimension is -% always 3 (i.e. [R G B]). In that case, make -% sure that you must specify RGB datatype to 128. +% img: a 3D matrix [x y z], or a 4D matrix with time +% series [x y z t]. When image is in RGB format, +% make sure that the size of 4th dimension is +% always 3 (i.e. [R G B]). In that case, make +% sure that you must specify RGB datatype to 128. % -% voxel_size (optional): Voxel size in millimeter for each -% dimension. Default is [1 1 1]. +% voxel_size (optional): Voxel size in millimeter for each +% dimension. Default is [1 1 1]. % -% origin (optional): The AC origin. Default is [0 0 0]. +% origin (optional): The AC origin. Default is [0 0 0]. % -% datatype (optional): Storage data type: -% 2 - uint8, 4 - int16, 8 - int32, 16 - float32, -% 64 - float64, 128 - RGB24 -% Default will use the data type of 'img' matrix -% For RGB image, you must specify it to 128. +% datatype (optional): Storage data type: +% 2 - uint8, 4 - int16, 8 - int32, 16 - float32, +% 64 - float64, 128 - RGB24 +% Default will use the data type of 'img' matrix +% For RGB image, you must specify it to 128. % -% description (optional): Description of data. Default is ''. +% description (optional): Description of data. Default is ''. % % e.g.: % origin = [33 44 13]; datatype = 64; @@ -39,172 +39,173 @@ % function ana = make_ana(varargin) - ana.img = varargin{1}; - dims = size(ana.img); - dims = [4 dims ones(1,8)]; - dims = dims(1:8); - - voxel_size = [0 ones(1,3) zeros(1,4)]; - origin = zeros(1,5); - descrip = ''; - - switch class(ana.img) - case 'uint8' - datatype = 2; - case 'int16' - datatype = 4; - case 'int32' - datatype = 8; - case 'single' - datatype = 16; - case 'double' - datatype = 64; - otherwise - error('Datatype is not supported by make_ana.'); - end - - if nargin > 1 & ~isempty(varargin{2}) - voxel_size(2:4) = double(varargin{2}); - end - - if nargin > 2 & ~isempty(varargin{3}) - origin(1:3) = double(varargin{3}); - end - - if nargin > 3 & ~isempty(varargin{4}) - datatype = double(varargin{4}); - - if datatype == 128 | datatype == 511 - dims(5) = []; - dims = [dims 1]; - end - end - - if nargin > 4 & ~isempty(varargin{5}) - descrip = varargin{5}; - end - - if ndims(ana.img) > 4 - error('NIfTI only allows a maximum of 4 Dimension matrix.'); - end - - maxval = round(double(max(ana.img(:)))); - minval = round(double(min(ana.img(:)))); - - ana.hdr = make_header(dims, voxel_size, origin, datatype, ... - descrip, maxval, minval); - ana.filetype = 0; - ana.ext = []; - ana.untouch = 1; - - switch ana.hdr.dime.datatype - case 2 + ana.img = varargin{1}; + dims = size(ana.img); + dims = [4 dims ones(1, 8)]; + dims = dims(1:8); + + voxel_size = [0 ones(1, 3) zeros(1, 4)]; + origin = zeros(1, 5); + descrip = ''; + + switch class(ana.img) + case 'uint8' + datatype = 2; + case 'int16' + datatype = 4; + case 'int32' + datatype = 8; + case 'single' + datatype = 16; + case 'double' + datatype = 64; + otherwise + error('Datatype is not supported by make_ana.'); + end + + if nargin > 1 & ~isempty(varargin{2}) + voxel_size(2:4) = double(varargin{2}); + end + + if nargin > 2 & ~isempty(varargin{3}) + origin(1:3) = double(varargin{3}); + end + + if nargin > 3 & ~isempty(varargin{4}) + datatype = double(varargin{4}); + + if datatype == 128 | datatype == 511 + dims(5) = []; + dims = [dims 1]; + end + end + + if nargin > 4 & ~isempty(varargin{5}) + descrip = varargin{5}; + end + + if ndims(ana.img) > 4 + error('NIfTI only allows a maximum of 4 Dimension matrix.'); + end + + maxval = round(double(max(ana.img(:)))); + minval = round(double(min(ana.img(:)))); + + ana.hdr = make_header(dims, voxel_size, origin, datatype, ... + descrip, maxval, minval); + ana.filetype = 0; + ana.ext = []; + ana.untouch = 1; + + switch ana.hdr.dime.datatype + case 2 ana.img = uint8(ana.img); - case 4 + case 4 ana.img = int16(ana.img); - case 8 + case 8 ana.img = int32(ana.img); - case 16 + case 16 ana.img = single(ana.img); - case 64 + case 64 ana.img = double(ana.img); - case 128 + case 128 ana.img = uint8(ana.img); - otherwise + otherwise error('Datatype is not supported by make_ana.'); - end - - return; % make_ana + end + return % make_ana -%--------------------------------------------------------------------- + % --------------------------------------------------------------------- function hdr = make_header(dims, voxel_size, origin, datatype, ... - descrip, maxval, minval) + descrip, maxval, minval) - hdr.hk = header_key; - hdr.dime = image_dimension(dims, voxel_size, datatype, maxval, minval); - hdr.hist = data_history(origin, descrip); - - return; % make_header + hdr.hk = header_key; + hdr.dime = image_dimension(dims, voxel_size, datatype, maxval, minval); + hdr.hist = data_history(origin, descrip); + return % make_header -%--------------------------------------------------------------------- + % --------------------------------------------------------------------- function hk = header_key - hk.sizeof_hdr = 348; % must be 348! - hk.data_type = ''; - hk.db_name = ''; - hk.extents = 0; - hk.session_error = 0; - hk.regular = 'r'; - hk.hkey_un0 = '0'; - - return; % header_key + hk.sizeof_hdr = 348; % must be 348! + hk.data_type = ''; + hk.db_name = ''; + hk.extents = 0; + hk.session_error = 0; + hk.regular = 'r'; + hk.hkey_un0 = '0'; + return % header_key -%--------------------------------------------------------------------- + % --------------------------------------------------------------------- function dime = image_dimension(dims, voxel_size, datatype, maxval, minval) - - dime.dim = dims; - dime.vox_units = 'mm'; - dime.cal_units = ''; - dime.unused1 = 0; - dime.datatype = datatype; - - switch dime.datatype - case 2, - dime.bitpix = 8; precision = 'uint8'; - case 4, - dime.bitpix = 16; precision = 'int16'; - case 8, - dime.bitpix = 32; precision = 'int32'; - case 16, - dime.bitpix = 32; precision = 'float32'; - case 64, - dime.bitpix = 64; precision = 'float64'; - case 128 - dime.bitpix = 24; precision = 'uint8'; - otherwise + + dime.dim = dims; + dime.vox_units = 'mm'; + dime.cal_units = ''; + dime.unused1 = 0; + dime.datatype = datatype; + + switch dime.datatype + case 2 + dime.bitpix = 8; + precision = 'uint8'; + case 4 + dime.bitpix = 16; + precision = 'int16'; + case 8 + dime.bitpix = 32; + precision = 'int32'; + case 16 + dime.bitpix = 32; + precision = 'float32'; + case 64 + dime.bitpix = 64; + precision = 'float64'; + case 128 + dime.bitpix = 24; + precision = 'uint8'; + otherwise error('Datatype is not supported by make_ana.'); - end - - dime.dim_un0 = 0; - dime.pixdim = voxel_size; - dime.vox_offset = 0; - dime.roi_scale = 1; - dime.funused1 = 0; - dime.funused2 = 0; - dime.cal_max = 0; - dime.cal_min = 0; - dime.compressed = 0; - dime.verified = 0; - dime.glmax = maxval; - dime.glmin = minval; - - return; % image_dimension - - -%--------------------------------------------------------------------- + end + + dime.dim_un0 = 0; + dime.pixdim = voxel_size; + dime.vox_offset = 0; + dime.roi_scale = 1; + dime.funused1 = 0; + dime.funused2 = 0; + dime.cal_max = 0; + dime.cal_min = 0; + dime.compressed = 0; + dime.verified = 0; + dime.glmax = maxval; + dime.glmin = minval; + + return % image_dimension + + % --------------------------------------------------------------------- function hist = data_history(origin, descrip) - - hist.descrip = descrip; - hist.aux_file = 'none'; - hist.orient = 0; - hist.originator = origin; - hist.generated = ''; - hist.scannum = ''; - hist.patient_id = ''; - hist.exp_date = ''; - hist.exp_time = ''; - hist.hist_un0 = ''; - hist.views = 0; - hist.vols_added = 0; - hist.start_field = 0; - hist.field_skip = 0; - hist.omax = 0; - hist.omin = 0; - hist.smax = 0; - hist.smin = 0; - - return; % data_history + hist.descrip = descrip; + hist.aux_file = 'none'; + hist.orient = 0; + hist.originator = origin; + hist.generated = ''; + hist.scannum = ''; + hist.patient_id = ''; + hist.exp_date = ''; + hist.exp_time = ''; + hist.hist_un0 = ''; + hist.views = 0; + hist.vols_added = 0; + hist.start_field = 0; + hist.field_skip = 0; + hist.omax = 0; + hist.omin = 0; + hist.smax = 0; + hist.smin = 0; + + return % data_history diff --git a/lib/NiftiTools/make_nii.m b/lib/NiftiTools/make_nii.m index 1af1c5e7..0e8c6b25 100644 --- a/lib/NiftiTools/make_nii.m +++ b/lib/NiftiTools/make_nii.m @@ -1,38 +1,38 @@ -% Make NIfTI structure specified by an N-D matrix. Usually, N is 3 for -% 3D matrix [x y z], or 4 for 4D matrix with time series [x y z t]. -% Optional parameters can also be included, such as: voxel_size, -% origin, datatype, and description. -% -% Once the NIfTI structure is made, it can be saved into NIfTI file -% using "save_nii" command (for more detail, type: help save_nii). -% +% Make NIfTI structure specified by an N-D matrix. Usually, N is 3 for +% 3D matrix [x y z], or 4 for 4D matrix with time series [x y z t]. +% Optional parameters can also be included, such as: voxel_size, +% origin, datatype, and description. +% +% Once the NIfTI structure is made, it can be saved into NIfTI file +% using "save_nii" command (for more detail, type: help save_nii). +% % Usage: nii = make_nii(img, [voxel_size], [origin], [datatype], [description]) % % Where: % -% img: Usually, img is a 3D matrix [x y z], or a 4D -% matrix with time series [x y z t]. However, -% NIfTI allows a maximum of 7D matrix. When the -% image is in RGB format, make sure that the size -% of 4th dimension is always 3 (i.e. [R G B]). In -% that case, make sure that you must specify RGB -% datatype, which is either 128 or 511. +% img: Usually, img is a 3D matrix [x y z], or a 4D +% matrix with time series [x y z t]. However, +% NIfTI allows a maximum of 7D matrix. When the +% image is in RGB format, make sure that the size +% of 4th dimension is always 3 (i.e. [R G B]). In +% that case, make sure that you must specify RGB +% datatype, which is either 128 or 511. % -% voxel_size (optional): Voxel size in millimeter for each -% dimension. Default is [1 1 1]. +% voxel_size (optional): Voxel size in millimeter for each +% dimension. Default is [1 1 1]. % -% origin (optional): The AC origin. Default is [0 0 0]. +% origin (optional): The AC origin. Default is [0 0 0]. % -% datatype (optional): Storage data type: -% 2 - uint8, 4 - int16, 8 - int32, 16 - float32, -% 32 - complex64, 64 - float64, 128 - RGB24, -% 256 - int8, 511 - RGB96, 512 - uint16, -% 768 - uint32, 1792 - complex128 -% Default will use the data type of 'img' matrix -% For RGB image, you must specify it to either 128 -% or 511. +% datatype (optional): Storage data type: +% 2 - uint8, 4 - int16, 8 - int32, 16 - float32, +% 32 - complex64, 64 - float64, 128 - RGB24, +% 256 - int8, 511 - RGB96, 512 - uint16, +% 768 - uint32, 1792 - complex128 +% Default will use the data type of 'img' matrix +% For RGB image, you must specify it to either 128 +% or 511. % -% description (optional): Description of data. Default is ''. +% description (optional): Description of data. Default is ''. % % e.g.: % origin = [33 44 13]; datatype = 64; @@ -44,213 +44,220 @@ % function nii = make_nii(varargin) - nii.img = varargin{1}; - dims = size(nii.img); - dims = [length(dims) dims ones(1,8)]; - dims = dims(1:8); + nii.img = varargin{1}; + dims = size(nii.img); + dims = [length(dims) dims ones(1, 8)]; + dims = dims(1:8); - voxel_size = [0 ones(1,7)]; - origin = zeros(1,5); - descrip = ''; + voxel_size = [0 ones(1, 7)]; + origin = zeros(1, 5); + descrip = ''; - switch class(nii.img) - case 'uint8' - datatype = 2; - case 'int16' - datatype = 4; - case 'int32' - datatype = 8; - case 'single' - if isreal(nii.img) - datatype = 16; - else - datatype = 32; - end - case 'double' - if isreal(nii.img) - datatype = 64; - else - datatype = 1792; - end - case 'int8' - datatype = 256; - case 'uint16' - datatype = 512; - case 'uint32' - datatype = 768; - otherwise - error('Datatype is not supported by make_nii.'); - end + switch class(nii.img) + case 'uint8' + datatype = 2; + case 'int16' + datatype = 4; + case 'int32' + datatype = 8; + case 'single' + if isreal(nii.img) + datatype = 16; + else + datatype = 32; + end + case 'double' + if isreal(nii.img) + datatype = 64; + else + datatype = 1792; + end + case 'int8' + datatype = 256; + case 'uint16' + datatype = 512; + case 'uint32' + datatype = 768; + otherwise + error('Datatype is not supported by make_nii.'); + end - if nargin > 1 & ~isempty(varargin{2}) - voxel_size(2:4) = double(varargin{2}); - end + if nargin > 1 & ~isempty(varargin{2}) + voxel_size(2:4) = double(varargin{2}); + end - if nargin > 2 & ~isempty(varargin{3}) - origin(1:3) = double(varargin{3}); - end + if nargin > 2 & ~isempty(varargin{3}) + origin(1:3) = double(varargin{3}); + end - if nargin > 3 & ~isempty(varargin{4}) - datatype = double(varargin{4}); + if nargin > 3 & ~isempty(varargin{4}) + datatype = double(varargin{4}); - if datatype == 128 | datatype == 511 - dims(5) = []; - dims(1) = dims(1) - 1; - dims = [dims 1]; - end - end + if datatype == 128 | datatype == 511 + dims(5) = []; + dims(1) = dims(1) - 1; + dims = [dims 1]; + end + end - if nargin > 4 & ~isempty(varargin{5}) - descrip = varargin{5}; - end + if nargin > 4 & ~isempty(varargin{5}) + descrip = varargin{5}; + end - if ndims(nii.img) > 7 - error('NIfTI only allows a maximum of 7 Dimension matrix.'); - end + if ndims(nii.img) > 7 + error('NIfTI only allows a maximum of 7 Dimension matrix.'); + end - maxval = round(double(max(nii.img(:)))); - minval = round(double(min(nii.img(:)))); + maxval = round(double(max(nii.img(:)))); + minval = round(double(min(nii.img(:)))); - nii.hdr = make_header(dims, voxel_size, origin, datatype, ... - descrip, maxval, minval); + nii.hdr = make_header(dims, voxel_size, origin, datatype, ... + descrip, maxval, minval); - switch nii.hdr.dime.datatype - case 2 + switch nii.hdr.dime.datatype + case 2 nii.img = uint8(nii.img); - case 4 + case 4 nii.img = int16(nii.img); - case 8 + case 8 nii.img = int32(nii.img); - case 16 + case 16 nii.img = single(nii.img); - case 32 + case 32 nii.img = single(nii.img); - case 64 + case 64 nii.img = double(nii.img); - case 128 + case 128 nii.img = uint8(nii.img); - case 256 + case 256 nii.img = int8(nii.img); - case 511 + case 511 img = double(nii.img(:)); - img = single((img - min(img))/(max(img) - min(img))); + img = single((img - min(img)) / (max(img) - min(img))); nii.img = reshape(img, size(nii.img)); nii.hdr.dime.glmax = double(max(img)); nii.hdr.dime.glmin = double(min(img)); - case 512 + case 512 nii.img = uint16(nii.img); - case 768 + case 768 nii.img = uint32(nii.img); - case 1792 + case 1792 nii.img = double(nii.img); - otherwise + otherwise error('Datatype is not supported by make_nii.'); - end + end - return; % make_nii + return % make_nii - -%--------------------------------------------------------------------- + % --------------------------------------------------------------------- function hdr = make_header(dims, voxel_size, origin, datatype, ... - descrip, maxval, minval) + descrip, maxval, minval) - hdr.hk = header_key; - hdr.dime = image_dimension(dims, voxel_size, datatype, maxval, minval); - hdr.hist = data_history(origin, descrip); - - return; % make_header + hdr.hk = header_key; + hdr.dime = image_dimension(dims, voxel_size, datatype, maxval, minval); + hdr.hist = data_history(origin, descrip); + return % make_header -%--------------------------------------------------------------------- + % --------------------------------------------------------------------- function hk = header_key - hk.sizeof_hdr = 348; % must be 348! - hk.data_type = ''; - hk.db_name = ''; - hk.extents = 0; - hk.session_error = 0; - hk.regular = 'r'; - hk.dim_info = 0; - - return; % header_key + hk.sizeof_hdr = 348; % must be 348! + hk.data_type = ''; + hk.db_name = ''; + hk.extents = 0; + hk.session_error = 0; + hk.regular = 'r'; + hk.dim_info = 0; + return % header_key -%--------------------------------------------------------------------- + % --------------------------------------------------------------------- function dime = image_dimension(dims, voxel_size, datatype, maxval, minval) - - dime.dim = dims; - dime.intent_p1 = 0; - dime.intent_p2 = 0; - dime.intent_p3 = 0; - dime.intent_code = 0; - dime.datatype = datatype; - - switch dime.datatype - case 2, - dime.bitpix = 8; precision = 'uint8'; - case 4, - dime.bitpix = 16; precision = 'int16'; - case 8, - dime.bitpix = 32; precision = 'int32'; - case 16, - dime.bitpix = 32; precision = 'float32'; - case 32, - dime.bitpix = 64; precision = 'float32'; - case 64, - dime.bitpix = 64; precision = 'float64'; - case 128 - dime.bitpix = 24; precision = 'uint8'; - case 256 - dime.bitpix = 8; precision = 'int8'; - case 511 - dime.bitpix = 96; precision = 'float32'; - case 512 - dime.bitpix = 16; precision = 'uint16'; - case 768 - dime.bitpix = 32; precision = 'uint32'; - case 1792, - dime.bitpix = 128; precision = 'float64'; - otherwise + + dime.dim = dims; + dime.intent_p1 = 0; + dime.intent_p2 = 0; + dime.intent_p3 = 0; + dime.intent_code = 0; + dime.datatype = datatype; + + switch dime.datatype + case 2 + dime.bitpix = 8; + precision = 'uint8'; + case 4 + dime.bitpix = 16; + precision = 'int16'; + case 8 + dime.bitpix = 32; + precision = 'int32'; + case 16 + dime.bitpix = 32; + precision = 'float32'; + case 32 + dime.bitpix = 64; + precision = 'float32'; + case 64 + dime.bitpix = 64; + precision = 'float64'; + case 128 + dime.bitpix = 24; + precision = 'uint8'; + case 256 + dime.bitpix = 8; + precision = 'int8'; + case 511 + dime.bitpix = 96; + precision = 'float32'; + case 512 + dime.bitpix = 16; + precision = 'uint16'; + case 768 + dime.bitpix = 32; + precision = 'uint32'; + case 1792 + dime.bitpix = 128; + precision = 'float64'; + otherwise error('Datatype is not supported by make_nii.'); - end - - dime.slice_start = 0; - dime.pixdim = voxel_size; - dime.vox_offset = 0; - dime.scl_slope = 0; - dime.scl_inter = 0; - dime.slice_end = 0; - dime.slice_code = 0; - dime.xyzt_units = 0; - dime.cal_max = 0; - dime.cal_min = 0; - dime.slice_duration = 0; - dime.toffset = 0; - dime.glmax = maxval; - dime.glmin = minval; - - return; % image_dimension + end + dime.slice_start = 0; + dime.pixdim = voxel_size; + dime.vox_offset = 0; + dime.scl_slope = 0; + dime.scl_inter = 0; + dime.slice_end = 0; + dime.slice_code = 0; + dime.xyzt_units = 0; + dime.cal_max = 0; + dime.cal_min = 0; + dime.slice_duration = 0; + dime.toffset = 0; + dime.glmax = maxval; + dime.glmin = minval; -%--------------------------------------------------------------------- + return % image_dimension + + % --------------------------------------------------------------------- function hist = data_history(origin, descrip) - - hist.descrip = descrip; - hist.aux_file = 'none'; - hist.qform_code = 0; - hist.sform_code = 0; - hist.quatern_b = 0; - hist.quatern_c = 0; - hist.quatern_d = 0; - hist.qoffset_x = 0; - hist.qoffset_y = 0; - hist.qoffset_z = 0; - hist.srow_x = zeros(1,4); - hist.srow_y = zeros(1,4); - hist.srow_z = zeros(1,4); - hist.intent_name = ''; - hist.magic = ''; - hist.originator = origin; - - return; % data_history + hist.descrip = descrip; + hist.aux_file = 'none'; + hist.qform_code = 0; + hist.sform_code = 0; + hist.quatern_b = 0; + hist.quatern_c = 0; + hist.quatern_d = 0; + hist.qoffset_x = 0; + hist.qoffset_y = 0; + hist.qoffset_z = 0; + hist.srow_x = zeros(1, 4); + hist.srow_y = zeros(1, 4); + hist.srow_z = zeros(1, 4); + hist.intent_name = ''; + hist.magic = ''; + hist.originator = origin; + + return % data_history diff --git a/lib/NiftiTools/mat_into_hdr.m b/lib/NiftiTools/mat_into_hdr.m index dc83a29c..90627cf9 100644 --- a/lib/NiftiTools/mat_into_hdr.m +++ b/lib/NiftiTools/mat_into_hdr.m @@ -1,83 +1,82 @@ -%MAT_INTO_HDR The old versions of SPM (any version before SPM5) store -% an affine matrix of the SPM Reoriented image into a matlab file -% (.mat extension). The file name of this SPM matlab file is the -% same as the SPM Reoriented image file (.img/.hdr extension). +% MAT_INTO_HDR The old versions of SPM (any version before SPM5) store +% an affine matrix of the SPM Reoriented image into a matlab file +% (.mat extension). The file name of this SPM matlab file is the +% same as the SPM Reoriented image file (.img/.hdr extension). % -% This program will convert the ANALYZE 7.5 SPM Reoriented image -% file into NIfTI format, and integrate the affine matrix in the -% SPM matlab file into its header file (.hdr extension). +% This program will convert the ANALYZE 7.5 SPM Reoriented image +% file into NIfTI format, and integrate the affine matrix in the +% SPM matlab file into its header file (.hdr extension). % -% WARNING: Before you run this program, please save the header -% file (.hdr extension) into another file name or into another -% folder location, because all header files (.hdr extension) -% will be overwritten after they are converted into NIfTI -% format. +% WARNING: Before you run this program, please save the header +% file (.hdr extension) into another file name or into another +% folder location, because all header files (.hdr extension) +% will be overwritten after they are converted into NIfTI +% format. % % Usage: mat_into_hdr(filename); % -% filename: file name(s) with .hdr or .mat file extension, like: -% '*.hdr', or '*.mat', or a single .hdr or .mat file. -% e.g. mat_into_hdr('T1.hdr') -% mat_into_hdr('*.mat') +% filename: file name(s) with .hdr or .mat file extension, like: +% '*.hdr', or '*.mat', or a single .hdr or .mat file. +% e.g. mat_into_hdr('T1.hdr') +% mat_into_hdr('*.mat') % % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % -%------------------------------------------------------------------------- +% ------------------------------------------------------------------------- function mat_into_hdr(files) - pn = fileparts(files); - file_lst = dir(files); - file_lst = {file_lst.name}; - file1 = file_lst{1}; - [p n e]= fileparts(file1); + pn = fileparts(files); + file_lst = dir(files); + file_lst = {file_lst.name}; + file1 = file_lst{1}; + [p n e] = fileparts(file1); - for i=1:length(file_lst) - [p n e]= fileparts(file_lst{i}); - disp(['working on file ', num2str(i) ,' of ', num2str(length(file_lst)), ': ', n,e]); - process=1; + for i = 1:length(file_lst) + [p n e] = fileparts(file_lst{i}); + disp(['working on file ', num2str(i), ' of ', num2str(length(file_lst)), ': ', n, e]); + process = 1; - if isequal(e,'.hdr') - mat=fullfile(pn, [n,'.mat']); - hdr=fullfile(pn, file_lst{i}); + if isequal(e, '.hdr') + mat = fullfile(pn, [n, '.mat']); + hdr = fullfile(pn, file_lst{i}); - if ~exist(mat,'file') - warning(['Cannot find file "',mat , '". File "', n, e, '" will not be processed.']); - process=0; - end - elseif isequal(e,'.mat') - hdr=fullfile(pn, [n,'.hdr']); - mat=fullfile(pn, file_lst{i}); - - if ~exist(hdr,'file') - warning(['Can not find file "',hdr , '". File "', n, e, '" will not be processed.']); - process=0; - end - else - warning(['Input file must have .mat or .hdr extension. File "', n, e, '" will not be processed.']); - process=0; + if ~exist(mat, 'file') + warning(['Cannot find file "', mat, '". File "', n, e, '" will not be processed.']); + process = 0; end + elseif isequal(e, '.mat') + hdr = fullfile(pn, [n, '.hdr']); + mat = fullfile(pn, file_lst{i}); - if process - load(mat); - R=M(1:3,1:3); - T=M(1:3,4); - T=R*ones(3,1)+T; - M(1:3,4)=T; + if ~exist(hdr, 'file') + warning(['Can not find file "', hdr, '". File "', n, e, '" will not be processed.']); + process = 0; + end + else + warning(['Input file must have .mat or .hdr extension. File "', n, e, '" will not be processed.']); + process = 0; + end - [h filetype fileprefix machine]=load_nii_hdr(hdr); - h.hist.qform_code=0; - h.hist.sform_code=1; - h.hist.srow_x=M(1,:); - h.hist.srow_y=M(2,:); - h.hist.srow_z=M(3,:); - h.hist.magic='ni1'; + if process + load(mat); + R = M(1:3, 1:3); + T = M(1:3, 4); + T = R * ones(3, 1) + T; + M(1:3, 4) = T; - fid = fopen(hdr,'w',machine); - save_nii_hdr(h,fid); - fclose(fid); - end - end + [h filetype fileprefix machine] = load_nii_hdr(hdr); + h.hist.qform_code = 0; + h.hist.sform_code = 1; + h.hist.srow_x = M(1, :); + h.hist.srow_y = M(2, :); + h.hist.srow_z = M(3, :); + h.hist.magic = 'ni1'; - return; % mat_into_hdr + fid = fopen(hdr, 'w', machine); + save_nii_hdr(h, fid); + fclose(fid); + end + end + return % mat_into_hdr diff --git a/lib/NiftiTools/pad_nii.m b/lib/NiftiTools/pad_nii.m index 6398f220..71cbefc9 100644 --- a/lib/NiftiTools/pad_nii.m +++ b/lib/NiftiTools/pad_nii.m @@ -1,6 +1,6 @@ % PAD_NII: Pad the NIfTI volume from any of the 6 sides % -% Usage: nii = pad_nii(nii, [option]) +% Usage: nii = pad_nii(nii, [option]) % % Inputs: % @@ -8,30 +8,30 @@ % % option - struct instructing how many voxel to be padded from which side. % -% option.pad_from_L = ( number of voxel ) -% option.pad_from_R = ( number of voxel ) -% option.pad_from_P = ( number of voxel ) -% option.pad_from_A = ( number of voxel ) -% option.pad_from_I = ( number of voxel ) -% option.pad_from_S = ( number of voxel ) -% option.bg = [0] +% option.pad_from_L = ( number of voxel ) +% option.pad_from_R = ( number of voxel ) +% option.pad_from_P = ( number of voxel ) +% option.pad_from_A = ( number of voxel ) +% option.pad_from_I = ( number of voxel ) +% option.pad_from_S = ( number of voxel ) +% option.bg = [0] % -% Options description in detail: -% ============================== +% Options description in detail: +% ============================== % -% pad_from_L: Number of voxels from Left side will be padded. +% pad_from_L: Number of voxels from Left side will be padded. % -% pad_from_R: Number of voxels from Right side will be padded. +% pad_from_R: Number of voxels from Right side will be padded. % -% pad_from_P: Number of voxels from Posterior side will be padded. +% pad_from_P: Number of voxels from Posterior side will be padded. % -% pad_from_A: Number of voxels from Anterior side will be padded. +% pad_from_A: Number of voxels from Anterior side will be padded. % -% pad_from_I: Number of voxels from Inferior side will be padded. +% pad_from_I: Number of voxels from Inferior side will be padded. % -% pad_from_S: Number of voxels from Superior side will be padded. +% pad_from_S: Number of voxels from Superior side will be padded. % -% bg: Background intensity, which is 0 by default. +% bg: Background intensity, which is 0 by default. % % NIfTI data format can be found on: http://nifti.nimh.nih.gov % @@ -39,104 +39,103 @@ % function nii = pad_nii(nii, opt) - dims = abs(nii.hdr.dime.dim(2:4)); - origin = abs(nii.hdr.hist.originator(1:3)); + dims = abs(nii.hdr.dime.dim(2:4)); + origin = abs(nii.hdr.hist.originator(1:3)); - if isempty(origin) | all(origin == 0) % according to SPM - origin = round((dims+1)/2); - end + if isempty(origin) | all(origin == 0) % according to SPM + origin = round((dims + 1) / 2); + end - pad_from_L = 0; - pad_from_R = 0; - pad_from_P = 0; - pad_from_A = 0; - pad_from_I = 0; - pad_from_S = 0; - bg = 0; + pad_from_L = 0; + pad_from_R = 0; + pad_from_P = 0; + pad_from_A = 0; + pad_from_I = 0; + pad_from_S = 0; + bg = 0; - if nargin > 1 & ~isempty(opt) - if ~isstruct(opt) - error('option argument should be a struct'); - end + if nargin > 1 & ~isempty(opt) + if ~isstruct(opt) + error('option argument should be a struct'); + end - if isfield(opt,'pad_from_L') - pad_from_L = round(opt.pad_from_L); + if isfield(opt, 'pad_from_L') + pad_from_L = round(opt.pad_from_L); - if pad_from_L >= origin(1) | pad_from_L < 0 - error('pad_from_L cannot be negative'); - end + if pad_from_L >= origin(1) | pad_from_L < 0 + error('pad_from_L cannot be negative'); end + end - if isfield(opt,'pad_from_P') - pad_from_P = round(opt.pad_from_P); + if isfield(opt, 'pad_from_P') + pad_from_P = round(opt.pad_from_P); - if pad_from_P >= origin(2) | pad_from_P < 0 - error('pad_from_P cannot be negative'); - end + if pad_from_P >= origin(2) | pad_from_P < 0 + error('pad_from_P cannot be negative'); end + end - if isfield(opt,'pad_from_I') - pad_from_I = round(opt.pad_from_I); + if isfield(opt, 'pad_from_I') + pad_from_I = round(opt.pad_from_I); - if pad_from_I >= origin(3) | pad_from_I < 0 - error('pad_from_I cannot be negative'); - end + if pad_from_I >= origin(3) | pad_from_I < 0 + error('pad_from_I cannot be negative'); end + end - if isfield(opt,'pad_from_R') - pad_from_R = round(opt.pad_from_R); + if isfield(opt, 'pad_from_R') + pad_from_R = round(opt.pad_from_R); - if pad_from_R > dims(1)-origin(1) | pad_from_R < 0 - error('pad_from_R cannot be negative'); - end + if pad_from_R > dims(1) - origin(1) | pad_from_R < 0 + error('pad_from_R cannot be negative'); end + end - if isfield(opt,'pad_from_A') - pad_from_A = round(opt.pad_from_A); + if isfield(opt, 'pad_from_A') + pad_from_A = round(opt.pad_from_A); - if pad_from_A > dims(2)-origin(2) | pad_from_A < 0 - error('pad_from_A cannot be negative'); - end + if pad_from_A > dims(2) - origin(2) | pad_from_A < 0 + error('pad_from_A cannot be negative'); end + end - if isfield(opt,'pad_from_S') - pad_from_S = round(opt.pad_from_S); - - if pad_from_S > dims(3)-origin(3) | pad_from_S < 0 - error('pad_from_S cannot be negative'); - end - end + if isfield(opt, 'pad_from_S') + pad_from_S = round(opt.pad_from_S); - if isfield(opt,'bg') - bg = opt.bg; + if pad_from_S > dims(3) - origin(3) | pad_from_S < 0 + error('pad_from_S cannot be negative'); end - end + end - blk = bg * ones( pad_from_L, dims(2), dims(3) ); - nii.img = cat(1, blk, nii.img); + if isfield(opt, 'bg') + bg = opt.bg; + end + end - blk = bg * ones( pad_from_R, dims(2), dims(3) ); - nii.img = cat(1, nii.img, blk); + blk = bg * ones(pad_from_L, dims(2), dims(3)); + nii.img = cat(1, blk, nii.img); - dims = size(nii.img); + blk = bg * ones(pad_from_R, dims(2), dims(3)); + nii.img = cat(1, nii.img, blk); - blk = bg * ones( dims(1), pad_from_P, dims(3) ); - nii.img = cat(2, blk, nii.img); + dims = size(nii.img); - blk = bg * ones( dims(1), pad_from_A, dims(3) ); - nii.img = cat(2, nii.img, blk); + blk = bg * ones(dims(1), pad_from_P, dims(3)); + nii.img = cat(2, blk, nii.img); - dims = size(nii.img); + blk = bg * ones(dims(1), pad_from_A, dims(3)); + nii.img = cat(2, nii.img, blk); - blk = bg * ones( dims(1), dims(2), pad_from_I ); - nii.img = cat(3, blk, nii.img); + dims = size(nii.img); - blk = bg * ones( dims(1), dims(2), pad_from_S ); - nii.img = cat(3, nii.img, blk); + blk = bg * ones(dims(1), dims(2), pad_from_I); + nii.img = cat(3, blk, nii.img); - nii = make_nii(nii.img, nii.hdr.dime.pixdim(2:4), ... - [origin(1)+pad_from_L origin(2)+pad_from_P origin(3)+pad_from_I], ... - nii.hdr.dime.datatype, nii.hdr.hist.descrip); + blk = bg * ones(dims(1), dims(2), pad_from_S); + nii.img = cat(3, nii.img, blk); - return; + nii = make_nii(nii.img, nii.hdr.dime.pixdim(2:4), ... + [origin(1) + pad_from_L origin(2) + pad_from_P origin(3) + pad_from_I], ... + nii.hdr.dime.datatype, nii.hdr.hist.descrip); + return diff --git a/lib/NiftiTools/reslice_nii.m b/lib/NiftiTools/reslice_nii.m index 2e43ad37..f58bc1cf 100644 --- a/lib/NiftiTools/reslice_nii.m +++ b/lib/NiftiTools/reslice_nii.m @@ -8,314 +8,317 @@ % The resliced NIfTI file will always be in RAS orientation. % % This program only supports real integer or floating-point data type. -% For other data type, the program will exit with an error message +% For other data type, the program will exit with an error message % "Transform of this NIFTI data is not supported by the program". % % Usage: reslice_nii(old_fn, new_fn, [voxel_size], [verbose], [bg], ... -% [method], [img_idx], [preferredForm]); +% [method], [img_idx], [preferredForm]); % -% old_fn - filename for original NIfTI file +% old_fn - filename for original NIfTI file % -% new_fn - filename for resliced NIfTI file +% new_fn - filename for resliced NIfTI file % % voxel_size (optional) - size of a voxel in millimeter along x y z -% direction for resliced NIfTI file. 'voxel_size' will use -% the minimum voxel_size in original NIfTI header, -% if it is default or empty. +% direction for resliced NIfTI file. 'voxel_size' will use +% the minimum voxel_size in original NIfTI header, +% if it is default or empty. % % verbose (optional) - 1, 0 -% 1: show transforming progress in percentage -% 2: progress will not be displayed -% 'verbose' is 1 if it is default or empty. +% 1: show transforming progress in percentage +% 2: progress will not be displayed +% 'verbose' is 1 if it is default or empty. % -% bg (optional) - background voxel intensity in any extra corner that -% is caused by 3D interpolation. 0 in most cases. 'bg' -% will be the average of two corner voxel intensities -% in original image volume, if it is default or empty. +% bg (optional) - background voxel intensity in any extra corner that +% is caused by 3D interpolation. 0 in most cases. 'bg' +% will be the average of two corner voxel intensities +% in original image volume, if it is default or empty. % -% method (optional) - 1, 2, or 3 -% 1: for Trilinear interpolation -% 2: for Nearest Neighbor interpolation -% 3: for Fischer's Bresenham interpolation -% 'method' is 1 if it is default or empty. +% method (optional) - 1, 2, or 3 +% 1: for Trilinear interpolation +% 2: for Nearest Neighbor interpolation +% 3: for Fischer's Bresenham interpolation +% 'method' is 1 if it is default or empty. % % img_idx (optional) - a numerical array of image volume indices. Only -% the specified volumes will be loaded. All available image -% volumes will be loaded, if it is default or empty. +% the specified volumes will be loaded. All available image +% volumes will be loaded, if it is default or empty. % -% The number of images scans can be obtained from get_nii_frame.m, -% or simply: hdr.dime.dim(5). +% The number of images scans can be obtained from get_nii_frame.m, +% or simply: hdr.dime.dim(5). % % preferredForm (optional) - selects which transformation from voxels -% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate -% "prefer sform or qform, but use others if preferred not present". -% Upper case indicate the program is forced to use the specificied -% tranform or fail loading. 'preferredForm' will be 's', if it is -% default or empty. - Jeff Gunter +% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate +% "prefer sform or qform, but use others if preferred not present". +% Upper case indicate the program is forced to use the specificied +% tranform or fail loading. 'preferredForm' will be 's', if it is +% default or empty. - Jeff Gunter % % NIFTI data format can be found on: http://nifti.nimh.nih.gov -% +% % - Jimmy Shen (jshen@research.baycrest.org) % function reslice_nii(old_fn, new_fn, voxel_size, verbose, bg, method, img_idx, preferredForm) - if ~exist('old_fn','var') | ~exist('new_fn','var') - error('Usage: reslice_nii(old_fn, new_fn, [voxel_size], [verbose], [bg], [method], [img_idx])'); - end - - if ~exist('method','var') | isempty(method) - method = 1; - end + if ~exist('old_fn', 'var') | ~exist('new_fn', 'var') + error('Usage: reslice_nii(old_fn, new_fn, [voxel_size], [verbose], [bg], [method], [img_idx])'); + end - if ~exist('img_idx','var') | isempty(img_idx) - img_idx = []; - end + if ~exist('method', 'var') | isempty(method) + method = 1; + end - if ~exist('verbose','var') | isempty(verbose) - verbose = 1; - end + if ~exist('img_idx', 'var') | isempty(img_idx) + img_idx = []; + end - if ~exist('preferredForm','var') | isempty(preferredForm) - preferredForm= 's'; % Jeff - end + if ~exist('verbose', 'var') | isempty(verbose) + verbose = 1; + end - nii = load_nii_no_xform(old_fn, img_idx, 0, preferredForm); + if ~exist('preferredForm', 'var') | isempty(preferredForm) + preferredForm = 's'; % Jeff + end - if ~ismember(nii.hdr.dime.datatype, [2,4,8,16,64,256,512,768]) - error('Transform of this NIFTI data is not supported by the program.'); - end + nii = load_nii_no_xform(old_fn, img_idx, 0, preferredForm); - if ~exist('voxel_size','var') | isempty(voxel_size) - voxel_size = abs(min(nii.hdr.dime.pixdim(2:4)))*ones(1,3); - elseif length(voxel_size) < 3 - voxel_size = abs(voxel_size(1))*ones(1,3); - end + if ~ismember(nii.hdr.dime.datatype, [2, 4, 8, 16, 64, 256, 512, 768]) + error('Transform of this NIFTI data is not supported by the program.'); + end - if ~exist('bg','var') | isempty(bg) - bg = mean([nii.img(1) nii.img(end)]); - end + if ~exist('voxel_size', 'var') | isempty(voxel_size) + voxel_size = abs(min(nii.hdr.dime.pixdim(2:4))) * ones(1, 3); + elseif length(voxel_size) < 3 + voxel_size = abs(voxel_size(1)) * ones(1, 3); + end - old_M = nii.hdr.hist.old_affine; + if ~exist('bg', 'var') | isempty(bg) + bg = mean([nii.img(1) nii.img(end)]); + end - if nii.hdr.dime.dim(5) > 1 - for i = 1:nii.hdr.dime.dim(5) - if verbose - fprintf('Reslicing %d of %d volumes.\n', i, nii.hdr.dime.dim(5)); - end + old_M = nii.hdr.hist.old_affine; - [img(:,:,:,i) M] = ... - affine(nii.img(:,:,:,i), old_M, voxel_size, verbose, bg, method); + if nii.hdr.dime.dim(5) > 1 + for i = 1:nii.hdr.dime.dim(5) + if verbose + fprintf('Reslicing %d of %d volumes.\n', i, nii.hdr.dime.dim(5)); end - else - [img M] = affine(nii.img, old_M, voxel_size, verbose, bg, method); - end - - new_dim = size(img); - nii.img = img; - nii.hdr.dime.dim(2:4) = new_dim(1:3); - nii.hdr.dime.datatype = 16; - nii.hdr.dime.bitpix = 32; - nii.hdr.dime.pixdim(2:4) = voxel_size(:)'; - nii.hdr.dime.glmax = max(img(:)); - nii.hdr.dime.glmin = min(img(:)); - nii.hdr.hist.qform_code = 0; - nii.hdr.hist.sform_code = 1; - nii.hdr.hist.srow_x = M(1,:); - nii.hdr.hist.srow_y = M(2,:); - nii.hdr.hist.srow_z = M(3,:); - nii.hdr.hist.new_affine = M; - - save_nii(nii, new_fn); - - return; % reslice_nii - - -%-------------------------------------------------------------------- -function [nii] = load_nii_no_xform(filename, img_idx, old_RGB, preferredForm) - - if ~exist('filename','var'), - error('Usage: [nii] = load_nii(filename, [img_idx], [old_RGB])'); - end - - if ~exist('img_idx','var'), img_idx = []; end - if ~exist('old_RGB','var'), old_RGB = 0; end - if ~exist('preferredForm','var'), preferredForm= 's'; end % Jeff - - v = version; - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end + [img(:, :, :, i) M] = ... + affine(nii.img(:, :, :, i), old_M, voxel_size, verbose, bg, method); + end + else + [img M] = affine(nii.img, old_M, voxel_size, verbose, bg, method); + end + + new_dim = size(img); + nii.img = img; + nii.hdr.dime.dim(2:4) = new_dim(1:3); + nii.hdr.dime.datatype = 16; + nii.hdr.dime.bitpix = 32; + nii.hdr.dime.pixdim(2:4) = voxel_size(:)'; + nii.hdr.dime.glmax = max(img(:)); + nii.hdr.dime.glmin = min(img(:)); + nii.hdr.hist.qform_code = 0; + nii.hdr.hist.sform_code = 1; + nii.hdr.hist.srow_x = M(1, :); + nii.hdr.hist.srow_y = M(2, :); + nii.hdr.hist.srow_z = M(3, :); + nii.hdr.hist.new_affine = M; + + save_nii(nii, new_fn); + + return % reslice_nii + + % -------------------------------------------------------------------- +function [nii] = load_nii_no_xform(filename, img_idx, old_RGB, preferredForm) - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - % Read the dataset header - % - [nii.hdr,nii.filetype,nii.fileprefix,nii.machine] = load_nii_hdr(filename); - - % Read the header extension - % -% nii.ext = load_nii_ext(filename); - - % Read the dataset body - % - [nii.img,nii.hdr] = ... - load_nii_img(nii.hdr,nii.filetype,nii.fileprefix,nii.machine,img_idx,'','','',old_RGB); - - % Perform some of sform/qform transform - % -% nii = xform_nii(nii, preferredForm); - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - - % fix fileprefix so it doesn't point to temp location - % - nii.fileprefix = gzFileName(1:end-7); - rmdir(tmpDir,'s'); - end - - - hdr = nii.hdr; - - % NIFTI can have both sform and qform transform. This program - % will check sform_code prior to qform_code by default. - % - % If user specifys "preferredForm", user can then choose the - % priority. - Jeff - % - useForm=[]; % Jeff - - if isequal(preferredForm,'S') - if isequal(hdr.hist.sform_code,0) - error('User requires sform, sform not set in header'); - else - useForm='s'; - end - end % Jeff - - if isequal(preferredForm,'Q') - if isequal(hdr.hist.qform_code,0) - error('User requires sform, sform not set in header'); - else - useForm='q'; - end - end % Jeff - - if isequal(preferredForm,'s') - if hdr.hist.sform_code > 0 - useForm='s'; - elseif hdr.hist.qform_code > 0 - useForm='q'; - end - end % Jeff - - if isequal(preferredForm,'q') - if hdr.hist.qform_code > 0 - useForm='q'; - elseif hdr.hist.sform_code > 0 - useForm='s'; - end - end % Jeff - - if isequal(useForm,'s') - R = [hdr.hist.srow_x(1:3) - hdr.hist.srow_y(1:3) - hdr.hist.srow_z(1:3)]; - - T = [hdr.hist.srow_x(4) - hdr.hist.srow_y(4) - hdr.hist.srow_z(4)]; - - nii.hdr.hist.old_affine = [ [R;[0 0 0]] [T;1] ]; - - elseif isequal(useForm,'q') - b = hdr.hist.quatern_b; - c = hdr.hist.quatern_c; - d = hdr.hist.quatern_d; - - if 1.0-(b*b+c*c+d*d) < 0 - if abs(1.0-(b*b+c*c+d*d)) < 1e-5 - a = 0; - else - error('Incorrect quaternion values in this NIFTI data.'); - end + if ~exist('filename', 'var') + error('Usage: [nii] = load_nii(filename, [img_idx], [old_RGB])'); + end + + if ~exist('img_idx', 'var') + img_idx = []; + end + if ~exist('old_RGB', 'var') + old_RGB = 0; + end + if ~exist('preferredForm', 'var') + preferredForm = 's'; + end % Jeff + + v = version; + + % Check file extension. If .gz, unpack it into temp folder + % + if length(filename) > 2 & strcmp(filename(end - 2:end), '.gz') + + if ~strcmp(filename(end - 6:end), '.img.gz') & ... + ~strcmp(filename(end - 6:end), '.hdr.gz') & ... + ~strcmp(filename(end - 6:end), '.nii.gz') + + error('Please check filename.'); + end + + if str2num(v(1:3)) < 7.1 | ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + elseif strcmp(filename(end - 6:end), '.img.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.hdr.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.hdr.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.img.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.nii.gz') + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + filename = gunzip(filename, tmpDir); + filename = char(filename); % convert from cell to string + end + end + + % Read the dataset header + % + [nii.hdr, nii.filetype, nii.fileprefix, nii.machine] = load_nii_hdr(filename); + + % Read the header extension + % + % nii.ext = load_nii_ext(filename); + + % Read the dataset body + % + [nii.img, nii.hdr] = ... + load_nii_img(nii.hdr, nii.filetype, nii.fileprefix, nii.machine, img_idx, '', '', '', old_RGB); + + % Perform some of sform/qform transform + % + % nii = xform_nii(nii, preferredForm); + + % Clean up after gunzip + % + if exist('gzFileName', 'var') + + % fix fileprefix so it doesn't point to temp location + % + nii.fileprefix = gzFileName(1:end - 7); + rmdir(tmpDir, 's'); + end + + hdr = nii.hdr; + + % NIFTI can have both sform and qform transform. This program + % will check sform_code prior to qform_code by default. + % + % If user specifys "preferredForm", user can then choose the + % priority. - Jeff + % + useForm = []; % Jeff + + if isequal(preferredForm, 'S') + if isequal(hdr.hist.sform_code, 0) + error('User requires sform, sform not set in header'); + else + useForm = 's'; + end + end % Jeff + + if isequal(preferredForm, 'Q') + if isequal(hdr.hist.qform_code, 0) + error('User requires sform, sform not set in header'); + else + useForm = 'q'; + end + end % Jeff + + if isequal(preferredForm, 's') + if hdr.hist.sform_code > 0 + useForm = 's'; + elseif hdr.hist.qform_code > 0 + useForm = 'q'; + end + end % Jeff + + if isequal(preferredForm, 'q') + if hdr.hist.qform_code > 0 + useForm = 'q'; + elseif hdr.hist.sform_code > 0 + useForm = 's'; + end + end % Jeff + + if isequal(useForm, 's') + R = [hdr.hist.srow_x(1:3) + hdr.hist.srow_y(1:3) + hdr.hist.srow_z(1:3)]; + + T = [hdr.hist.srow_x(4) + hdr.hist.srow_y(4) + hdr.hist.srow_z(4)]; + + nii.hdr.hist.old_affine = [[R; [0 0 0]] [T; 1]]; + + elseif isequal(useForm, 'q') + b = hdr.hist.quatern_b; + c = hdr.hist.quatern_c; + d = hdr.hist.quatern_d; + + if 1.0 - (b * b + c * c + d * d) < 0 + if abs(1.0 - (b * b + c * c + d * d)) < 1e-5 + a = 0; else - a = sqrt(1.0-(b*b+c*c+d*d)); + error('Incorrect quaternion values in this NIFTI data.'); end - - qfac = hdr.dime.pixdim(1); - i = hdr.dime.pixdim(2); - j = hdr.dime.pixdim(3); - k = qfac * hdr.dime.pixdim(4); - - R = [a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c - 2*b*c+2*a*d a*a+c*c-b*b-d*d 2*c*d-2*a*b - 2*b*d-2*a*c 2*c*d+2*a*b a*a+d*d-c*c-b*b]; - - T = [hdr.hist.qoffset_x - hdr.hist.qoffset_y - hdr.hist.qoffset_z]; - - nii.hdr.hist.old_affine = [ [R * diag([i j k]);[0 0 0]] [T;1] ]; - - elseif nii.filetype == 0 & exist([nii.fileprefix '.mat'],'file') - load([nii.fileprefix '.mat']); % old SPM affine matrix - R=M(1:3,1:3); - T=M(1:3,4); - T=R*ones(3,1)+T; - M(1:3,4)=T; - nii.hdr.hist.old_affine = M; - - else - M = diag(hdr.dime.pixdim(2:5)); - M(1:3,4) = -M(1:3,1:3)*(hdr.hist.originator(1:3)-1)'; - M(4,4) = 1; - nii.hdr.hist.old_affine = M; - end - - return % load_nii_no_xform - + else + a = sqrt(1.0 - (b * b + c * c + d * d)); + end + + qfac = hdr.dime.pixdim(1); + i = hdr.dime.pixdim(2); + j = hdr.dime.pixdim(3); + k = qfac * hdr.dime.pixdim(4); + + R = [a * a + b * b - c * c - d * d 2 * b * c - 2 * a * d 2 * b * d + 2 * a * c + 2 * b * c + 2 * a * d a * a + c * c - b * b - d * d 2 * c * d - 2 * a * b + 2 * b * d - 2 * a * c 2 * c * d + 2 * a * b a * a + d * d - c * c - b * b]; + + T = [hdr.hist.qoffset_x + hdr.hist.qoffset_y + hdr.hist.qoffset_z]; + + nii.hdr.hist.old_affine = [[R * diag([i j k]); [0 0 0]] [T; 1]]; + + elseif nii.filetype == 0 & exist([nii.fileprefix '.mat'], 'file') + load([nii.fileprefix '.mat']); % old SPM affine matrix + R = M(1:3, 1:3); + T = M(1:3, 4); + T = R * ones(3, 1) + T; + M(1:3, 4) = T; + nii.hdr.hist.old_affine = M; + + else + M = diag(hdr.dime.pixdim(2:5)); + M(1:3, 4) = -M(1:3, 1:3) * (hdr.hist.originator(1:3) - 1)'; + M(4, 4) = 1; + nii.hdr.hist.old_affine = M; + end + + return % load_nii_no_xform diff --git a/lib/NiftiTools/rri_file_menu.m b/lib/NiftiTools/rri_file_menu.m index ec54bf34..f8802419 100644 --- a/lib/NiftiTools/rri_file_menu.m +++ b/lib/NiftiTools/rri_file_menu.m @@ -8,172 +8,168 @@ % % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % -%-------------------------------------------------------------------- +% -------------------------------------------------------------------- function rri_file_menu(action, varargin) - if isnumeric(action) - fig = action; - action = 'init'; - end - - % clear the message line, - % - h = findobj(gcf,'Tag','MessageLine'); - set(h,'String',''); - - if ~strcmp(action, 'init') - set(gcbf, 'InvertHardcopy','off'); -% set(gcbf, 'PaperPositionMode','auto'); - end - - switch action - case {'init'} - if nargin > 1 - init(fig, 1); % no 'close' menu - else - init(fig, 0); - end - case {'print_fig'} - printdlg(gcbf); - case {'copy_fig'} - copy_fig; - case {'export_fig'} - export_fig; - end - - return % rri_file_menu - - -%------------------------------------------------ -% -% Create (or append) File menu -% -function init(fig, no_close) - - % search for file menu - % - h_file = []; - menuitems = findobj(fig, 'type', 'uimenu'); - - for i=1:length(menuitems) - filelabel = get(menuitems(i),'label'); - - if strcmpi(strrep(filelabel, '&', ''), 'file') - h_file = menuitems(i); - break; - end - end - - set(fig, 'menubar', 'none'); - - if isempty(h_file) - if isempty(menuitems) - h_file = uimenu('parent', fig, 'label', 'File'); + if isnumeric(action) + fig = action; + action = 'init'; + end + + % clear the message line, + % + h = findobj(gcf, 'Tag', 'MessageLine'); + set(h, 'String', ''); + + if ~strcmp(action, 'init') + set(gcbf, 'InvertHardcopy', 'off'); + % set(gcbf, 'PaperPositionMode','auto'); + end + + switch action + case {'init'} + if nargin > 1 + init(fig, 1); % no 'close' menu else - h_file = uimenu('parent', fig, 'label', 'Copy Figure'); + init(fig, 0); end + case {'print_fig'} + printdlg(gcbf); + case {'copy_fig'} + copy_fig; + case {'export_fig'} + export_fig; + end + + return % rri_file_menu + + % ------------------------------------------------ + % + % Create (or append) File menu + % +function init(fig, no_close) - h1 = uimenu('parent', h_file, ... - 'callback','rri_file_menu(''copy_fig'');', ... - 'label','Copy to Clipboard'); - else - h1 = uimenu('parent', h_file, ... - 'callback','rri_file_menu(''copy_fig'');', ... - 'separator','on', ... - 'label','Copy to Clipboard'); - end - - h2 = uimenu(h_file, ... - 'callback','pagesetupdlg(gcbf);', ... - 'label','Page Setup...'); - - h2 = uimenu(h_file, ... - 'callback','printpreview(gcbf);', ... - 'label','Print Preview...'); - - h2 = uimenu('parent', h_file, ... - 'callback','printdlg(gcbf);', ... - 'label','Print Figure ...'); - - h2 = uimenu('parent', h_file, ... - 'callback','rri_file_menu(''export_fig'');', ... - 'label','Save Figure ...'); - - arch = computer; - if ~strcmpi(arch(1:2),'PC') - set(h1, 'enable', 'off'); - end - - if ~no_close - h1 = uimenu('parent', h_file, ... - 'callback','close(gcbf);', ... - 'separator','on', ... - 'label','Close'); - end - - return; % init - - -%------------------------------------------------ -% -% Copy to clipboard -% + % search for file menu + % + h_file = []; + menuitems = findobj(fig, 'type', 'uimenu'); + + for i = 1:length(menuitems) + filelabel = get(menuitems(i), 'label'); + + if strcmpi(strrep(filelabel, '&', ''), 'file') + h_file = menuitems(i); + break + end + end + + set(fig, 'menubar', 'none'); + + if isempty(h_file) + if isempty(menuitems) + h_file = uimenu('parent', fig, 'label', 'File'); + else + h_file = uimenu('parent', fig, 'label', 'Copy Figure'); + end + + h1 = uimenu('parent', h_file, ... + 'callback', 'rri_file_menu(''copy_fig'');', ... + 'label', 'Copy to Clipboard'); + else + h1 = uimenu('parent', h_file, ... + 'callback', 'rri_file_menu(''copy_fig'');', ... + 'separator', 'on', ... + 'label', 'Copy to Clipboard'); + end + + h2 = uimenu(h_file, ... + 'callback', 'pagesetupdlg(gcbf);', ... + 'label', 'Page Setup...'); + + h2 = uimenu(h_file, ... + 'callback', 'printpreview(gcbf);', ... + 'label', 'Print Preview...'); + + h2 = uimenu('parent', h_file, ... + 'callback', 'printdlg(gcbf);', ... + 'label', 'Print Figure ...'); + + h2 = uimenu('parent', h_file, ... + 'callback', 'rri_file_menu(''export_fig'');', ... + 'label', 'Save Figure ...'); + + arch = computer; + if ~strcmpi(arch(1:2), 'PC') + set(h1, 'enable', 'off'); + end + + if ~no_close + h1 = uimenu('parent', h_file, ... + 'callback', 'close(gcbf);', ... + 'separator', 'on', ... + 'label', 'Close'); + end + + return % init + + % ------------------------------------------------ + % + % Copy to clipboard + % function copy_fig - arch = computer; - if(~strcmpi(arch(1:2),'PC')) - error('copy to clipboard can only be used under MS Windows'); - return; - end - - print -noui -dbitmap; + arch = computer; + if ~strcmpi(arch(1:2), 'PC') + error('copy to clipboard can only be used under MS Windows'); + return + end - return % copy_fig + print -noui -dbitmap; + return % copy_fig -%------------------------------------------------ -% -% Save as an image file -% + % ------------------------------------------------ + % + % Save as an image file + % function export_fig - curr = pwd; - if isempty(curr) - curr = filesep; - end - - [selected_file, selected_path] = rri_select_file(curr,'Save As'); + curr = pwd; + if isempty(curr) + curr = filesep; + end - if isempty(selected_file) | isempty(selected_path) - return; - end + [selected_file, selected_path] = rri_select_file(curr, 'Save As'); - filename = [selected_path selected_file]; + if isempty(selected_file) | isempty(selected_path) + return + end - if(exist(filename,'file')==2) % file exist + filename = [selected_path selected_file]; - dlg_title = 'Confirm File Overwrite'; - msg = ['File ',filename,' exist. Are you sure you want to overwrite it?']; - response = questdlg(msg,dlg_title,'Yes','No','Yes'); + if exist(filename, 'file') == 2 % file exist - if(strcmp(response,'No')) - return; - end + dlg_title = 'Confirm File Overwrite'; + msg = ['File ', filename, ' exist. Are you sure you want to overwrite it?']; + response = questdlg(msg, dlg_title, 'Yes', 'No', 'Yes'); - end + if strcmp(response, 'No') + return + end - old_pointer = get(gcbf,'pointer'); - set(gcbf,'pointer','watch'); + end - try - saveas(gcbf,filename); - catch - msg = 'ERROR: Cannot save file'; - set(findobj(gcf,'Tag','MessageLine'),'String',msg); - end + old_pointer = get(gcbf, 'pointer'); + set(gcbf, 'pointer', 'watch'); - set(gcbf,'pointer',old_pointer); + try + saveas(gcbf, filename); + catch + msg = 'ERROR: Cannot save file'; + set(findobj(gcf, 'Tag', 'MessageLine'), 'String', msg); + end - return; % export_fig + set(gcbf, 'pointer', old_pointer); + return % export_fig diff --git a/lib/NiftiTools/rri_orient.m b/lib/NiftiTools/rri_orient.m index 9efcc3f3..abf57752 100644 --- a/lib/NiftiTools/rri_orient.m +++ b/lib/NiftiTools/rri_orient.m @@ -3,104 +3,102 @@ % Usage: nii = rri_orient(nii); % Jimmy Shen (jimmy@rotman-baycrest.on.ca), 26-APR-04 -%___________________________________________________________________ +% ___________________________________________________________________ function [nii, orient, pattern] = rri_orient(nii, varargin) - if nargin > 1 - pattern = varargin{1}; - else - pattern = []; - end - - if(nargin > 2) - orient = varargin{2}; - if(length(find(orient>6)) || length(find(orient<1))) %value checking - orient=[1 2 3]; %set to default if bogus values set - end - else - orient = [1 2 3]; - end - - - dim = double(nii.hdr.dime.dim([2:4])); - - if ~isempty(pattern) & ~isequal(length(pattern), prod(dim)) - return; - end - - % get orient of the current image - % - if isequal(orient, [1 2 3]) + if nargin > 1 + pattern = varargin{1}; + else + pattern = []; + end + + if nargin > 2 + orient = varargin{2}; + if length(find(orient > 6)) || length(find(orient < 1)) % value checking + orient = [1 2 3]; % set to default if bogus values set + end + else + orient = [1 2 3]; + end + + dim = double(nii.hdr.dime.dim([2:4])); + + if ~isempty(pattern) & ~isequal(length(pattern), prod(dim)) + return + end + + % get orient of the current image + % + if isequal(orient, [1 2 3]) orient = rri_orient_ui; pause(.1); - end - - % no need for conversion - % - if isequal(orient, [1 2 3]) - return; - end - - if isempty(pattern) - pattern = 1:prod(dim); - end - - pattern = reshape(pattern, dim); - img = nii.img; - - % calculate after flip orient - % - rot_orient = mod(orient + 2, 3) + 1; - - % do flip: - % - flip_orient = orient - rot_orient; - - for i = 1:3 - if flip_orient(i) - pattern = flipdim(pattern, i); - img = flipdim(img, i); - end - end - - % get index of orient (do inverse) - % - [tmp rot_orient] = sort(rot_orient); - - % do rotation: - % - pattern = permute(pattern, rot_orient); - img = permute(img, [rot_orient 4 5 6]); - - % rotate resolution, or 'dim' - % - new_dim = nii.hdr.dime.dim([2:4]); - new_dim = new_dim(rot_orient); - nii.hdr.dime.dim([2:4]) = new_dim; - - % rotate voxel_size, or 'pixdim' - % - tmp = nii.hdr.dime.pixdim([2:4]); - tmp = tmp(rot_orient); - nii.hdr.dime.pixdim([2:4]) = tmp; - - % re-calculate originator - % - tmp = nii.hdr.hist.originator([1:3]); - tmp = tmp(rot_orient); - flip_orient = flip_orient(rot_orient); - - for i = 1:3 - if flip_orient(i) & ~isequal(double(tmp(i)), 0) - tmp(i) = int16(double(new_dim(i)) - double(tmp(i)) + 1); - end - end - - nii.hdr.hist.originator([1:3]) = tmp; - - nii.img = img; - pattern = pattern(:); - - return; % rri_orient - + end + + % no need for conversion + % + if isequal(orient, [1 2 3]) + return + end + + if isempty(pattern) + pattern = 1:prod(dim); + end + + pattern = reshape(pattern, dim); + img = nii.img; + + % calculate after flip orient + % + rot_orient = mod(orient + 2, 3) + 1; + + % do flip: + % + flip_orient = orient - rot_orient; + + for i = 1:3 + if flip_orient(i) + pattern = flipdim(pattern, i); + img = flipdim(img, i); + end + end + + % get index of orient (do inverse) + % + [tmp rot_orient] = sort(rot_orient); + + % do rotation: + % + pattern = permute(pattern, rot_orient); + img = permute(img, [rot_orient 4 5 6]); + + % rotate resolution, or 'dim' + % + new_dim = nii.hdr.dime.dim([2:4]); + new_dim = new_dim(rot_orient); + nii.hdr.dime.dim([2:4]) = new_dim; + + % rotate voxel_size, or 'pixdim' + % + tmp = nii.hdr.dime.pixdim([2:4]); + tmp = tmp(rot_orient); + nii.hdr.dime.pixdim([2:4]) = tmp; + + % re-calculate originator + % + tmp = nii.hdr.hist.originator([1:3]); + tmp = tmp(rot_orient); + flip_orient = flip_orient(rot_orient); + + for i = 1:3 + if flip_orient(i) & ~isequal(double(tmp(i)), 0) + tmp(i) = int16(double(new_dim(i)) - double(tmp(i)) + 1); + end + end + + nii.hdr.hist.originator([1:3]) = tmp; + + nii.img = img; + pattern = pattern(:); + + return % rri_orient diff --git a/lib/NiftiTools/rri_orient_ui.m b/lib/NiftiTools/rri_orient_ui.m index 97cd1fbd..01611b31 100644 --- a/lib/NiftiTools/rri_orient_ui.m +++ b/lib/NiftiTools/rri_orient_ui.m @@ -1,251 +1,248 @@ % Return orientation of the current image: -% orient is orientation 1x3 matrix, in that: -% Three elements represent: [x y z] -% Element value: 1 - Left to Right; 2 - Posterior to Anterior; -% 3 - Inferior to Superior; 4 - Right to Left; -% 5 - Anterior to Posterior; 6 - Superior to Inferior; +% orient is orientation 1x3 matrix, in that: +% Three elements represent: [x y z] +% Element value: 1 - Left to Right; 2 - Posterior to Anterior; +% 3 - Inferior to Superior; 4 - Right to Left; +% 5 - Anterior to Posterior; 6 - Superior to Inferior; % e.g.: -% Standard RAS Orientation: [1 2 3] -% Standard RHOS Orientation: [2 4 3] +% Standard RAS Orientation: [1 2 3] +% Standard RHOS Orientation: [2 4 3] % Jimmy Shen (jimmy@rotman-baycrest.on.ca), 26-APR-04 % function orient = rri_orient_ui(varargin) - if nargin == 0 - init; - orient_ui_fig = gcf; - uiwait; % wait for user finish + if nargin == 0 + init; + orient_ui_fig = gcf; + uiwait; % wait for user finish - orient = getappdata(gcf, 'orient'); + orient = getappdata(gcf, 'orient'); - if isempty(orient) - orient = [1 2 3]; - end + if isempty(orient) + orient = [1 2 3]; + end - if ishandle(orient_ui_fig) - close(gcf); - end + if ishandle(orient_ui_fig) + close(gcf); + end - return; - end + return + end - action = varargin{1}; + action = varargin{1}; - if strcmp(action, 'done') - click_done; - elseif strcmp(action, 'cancel') - uiresume; - end + if strcmp(action, 'done') + click_done; + elseif strcmp(action, 'cancel') + uiresume; + end - return; % rri_orient_ui + return % rri_orient_ui - -%---------------------------------------------------------------------- + % ---------------------------------------------------------------------- function init - save_setting_status = 'on'; - rri_orient_pos = []; - - try - load('pls_profile'); - catch - end - - try - load('rri_pos_profile'); - catch - end + save_setting_status = 'on'; + rri_orient_pos = []; + + try + load('pls_profile'); + catch + end + + try + load('rri_pos_profile'); + catch + end - if ~isempty(rri_orient_pos) & strcmp(save_setting_status,'on') - - pos = rri_orient_pos; - - else - - w = 0.35; - h = 0.4; - x = (1-w)/2; - y = (1-h)/2; - - pos = [x y w h]; + if ~isempty(rri_orient_pos) & strcmp(save_setting_status, 'on') + + pos = rri_orient_pos; + + else + + w = 0.35; + h = 0.4; + x = (1 - w) / 2; + y = (1 - h) / 2; + + pos = [x y w h]; - end - - handles.figure = figure('Color',[0.8 0.8 0.8], ... - 'Units','normal', ... - 'Name', 'Convert to standard RAS orientation', ... - 'NumberTitle','off', ... - 'MenuBar','none', ... - 'Position',pos, ... - 'WindowStyle', 'normal', ... - 'ToolBar','none'); - - h0 = handles.figure; - Font.FontUnits = 'point'; - Font.FontSize = 12; - - margin = .1; - line_num = 6; - line_ht = (1 - margin*2) / line_num; - - x = margin; - y = 1 - margin - line_ht; - w = 1 - margin * 2; - h = line_ht * .7; - - pos = [x y w h]; - - handles.Ttit = uicontrol('parent', h0, ... - 'style','text', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'background', [0.8 0.8 0.8], ... - 'string', 'Please input orientation of the current image:'); - - y = y - line_ht; - w = .2; - - pos = [x y w h]; - - handles.Tx_orient = uicontrol('parent', h0, ... - 'style','text', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'background', [0.8 0.8 0.8], ... - 'string', 'X Axes:'); - - y = y - line_ht; - - pos = [x y w h]; - - handles.Ty_orient = uicontrol('parent', h0, ... - 'style','text', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'background', [0.8 0.8 0.8], ... - 'string', 'Y Axes:'); - - y = y - line_ht; - - pos = [x y w h]; - - handles.Tz_orient = uicontrol('parent', h0, ... - 'style','text', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'background', [0.8 0.8 0.8], ... - 'string', 'Z Axes:'); - - choice = { 'From Left to Right', 'From Posterior to Anterior', ... - 'From Inferior to Superior', 'From Right to Left', ... - 'From Anterior to Posterior', 'From Superior to Inferior' }; - - y = 1 - margin - line_ht; - y = y - line_ht; - w = 1 - margin - x - w; - x = 1 - margin - w; - - pos = [x y w h]; - - handles.x_orient = uicontrol('parent', h0, ... - 'style','popupmenu', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'string', choice, ... - 'value', 1, ... - 'background', [1 1 1]); - - y = y - line_ht; - - pos = [x y w h]; - - handles.y_orient = uicontrol('parent', h0, ... - 'style','popupmenu', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'string', choice, ... - 'value', 2, ... - 'background', [1 1 1]); - - y = y - line_ht; - - pos = [x y w h]; - - handles.z_orient = uicontrol('parent', h0, ... - 'style','popupmenu', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'string', choice, ... - 'value', 3, ... - 'background', [1 1 1]); - - x = margin; - y = y - line_ht * 1.5; - w = .3; - - pos = [x y w h]; - - handles.done = uicontrol('parent', h0, ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','center',... - 'callback', 'rri_orient_ui(''done'');', ... - 'string', 'Done'); - - x = 1 - margin - w; - - pos = [x y w h]; - - handles.cancel = uicontrol('parent', h0, ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','center',... - 'callback', 'rri_orient_ui(''cancel'');', ... - 'string', 'Cancel'); - - setappdata(h0, 'handles', handles); - setappdata(h0, 'orient', [1 2 3]); - - return; % init - - -%---------------------------------------------------------------------- + end + + handles.figure = figure('Color', [0.8 0.8 0.8], ... + 'Units', 'normal', ... + 'Name', 'Convert to standard RAS orientation', ... + 'NumberTitle', 'off', ... + 'MenuBar', 'none', ... + 'Position', pos, ... + 'WindowStyle', 'normal', ... + 'ToolBar', 'none'); + + h0 = handles.figure; + Font.FontUnits = 'point'; + Font.FontSize = 12; + + margin = .1; + line_num = 6; + line_ht = (1 - margin * 2) / line_num; + + x = margin; + y = 1 - margin - line_ht; + w = 1 - margin * 2; + h = line_ht * .7; + + pos = [x y w h]; + + handles.Ttit = uicontrol('parent', h0, ... + 'style', 'text', ... + 'unit', 'normal', ... + Font, ... + 'Position', pos, ... + 'HorizontalAlignment', 'left', ... + 'background', [0.8 0.8 0.8], ... + 'string', 'Please input orientation of the current image:'); + + y = y - line_ht; + w = .2; + + pos = [x y w h]; + + handles.Tx_orient = uicontrol('parent', h0, ... + 'style', 'text', ... + 'unit', 'normal', ... + Font, ... + 'Position', pos, ... + 'HorizontalAlignment', 'left', ... + 'background', [0.8 0.8 0.8], ... + 'string', 'X Axes:'); + + y = y - line_ht; + + pos = [x y w h]; + + handles.Ty_orient = uicontrol('parent', h0, ... + 'style', 'text', ... + 'unit', 'normal', ... + Font, ... + 'Position', pos, ... + 'HorizontalAlignment', 'left', ... + 'background', [0.8 0.8 0.8], ... + 'string', 'Y Axes:'); + + y = y - line_ht; + + pos = [x y w h]; + + handles.Tz_orient = uicontrol('parent', h0, ... + 'style', 'text', ... + 'unit', 'normal', ... + Font, ... + 'Position', pos, ... + 'HorizontalAlignment', 'left', ... + 'background', [0.8 0.8 0.8], ... + 'string', 'Z Axes:'); + + choice = { 'From Left to Right', 'From Posterior to Anterior', ... + 'From Inferior to Superior', 'From Right to Left', ... + 'From Anterior to Posterior', 'From Superior to Inferior' }; + + y = 1 - margin - line_ht; + y = y - line_ht; + w = 1 - margin - x - w; + x = 1 - margin - w; + + pos = [x y w h]; + + handles.x_orient = uicontrol('parent', h0, ... + 'style', 'popupmenu', ... + 'unit', 'normal', ... + Font, ... + 'Position', pos, ... + 'HorizontalAlignment', 'left', ... + 'string', choice, ... + 'value', 1, ... + 'background', [1 1 1]); + + y = y - line_ht; + + pos = [x y w h]; + + handles.y_orient = uicontrol('parent', h0, ... + 'style', 'popupmenu', ... + 'unit', 'normal', ... + Font, ... + 'Position', pos, ... + 'HorizontalAlignment', 'left', ... + 'string', choice, ... + 'value', 2, ... + 'background', [1 1 1]); + + y = y - line_ht; + + pos = [x y w h]; + + handles.z_orient = uicontrol('parent', h0, ... + 'style', 'popupmenu', ... + 'unit', 'normal', ... + Font, ... + 'Position', pos, ... + 'HorizontalAlignment', 'left', ... + 'string', choice, ... + 'value', 3, ... + 'background', [1 1 1]); + + x = margin; + y = y - line_ht * 1.5; + w = .3; + + pos = [x y w h]; + + handles.done = uicontrol('parent', h0, ... + 'unit', 'normal', ... + Font, ... + 'Position', pos, ... + 'HorizontalAlignment', 'center', ... + 'callback', 'rri_orient_ui(''done'');', ... + 'string', 'Done'); + + x = 1 - margin - w; + + pos = [x y w h]; + + handles.cancel = uicontrol('parent', h0, ... + 'unit', 'normal', ... + Font, ... + 'Position', pos, ... + 'HorizontalAlignment', 'center', ... + 'callback', 'rri_orient_ui(''cancel'');', ... + 'string', 'Cancel'); + + setappdata(h0, 'handles', handles); + setappdata(h0, 'orient', [1 2 3]); + + return % init + + % ---------------------------------------------------------------------- function click_done - handles = getappdata(gcf, 'handles'); - - x_orient = get(handles.x_orient, 'value'); - y_orient = get(handles.y_orient, 'value'); - z_orient = get(handles.z_orient, 'value'); + handles = getappdata(gcf, 'handles'); - orient = [x_orient y_orient z_orient]; - test_orient = [orient, orient + 3]; - test_orient = mod(test_orient, 3); + x_orient = get(handles.x_orient, 'value'); + y_orient = get(handles.y_orient, 'value'); + z_orient = get(handles.z_orient, 'value'); - if length(unique(test_orient)) ~= 3 - msgbox('Please don''t choose same or opposite direction','Error','modal'); - return; - end + orient = [x_orient y_orient z_orient]; + test_orient = [orient, orient + 3]; + test_orient = mod(test_orient, 3); - setappdata(gcf, 'orient', [x_orient y_orient z_orient]); - uiresume; + if length(unique(test_orient)) ~= 3 + msgbox('Please don''t choose same or opposite direction', 'Error', 'modal'); + return + end - return; % click_done + setappdata(gcf, 'orient', [x_orient y_orient z_orient]); + uiresume; + return % click_done diff --git a/lib/NiftiTools/rri_xhair.m b/lib/NiftiTools/rri_xhair.m index 9a9951ad..d0c66e70 100644 --- a/lib/NiftiTools/rri_xhair.m +++ b/lib/NiftiTools/rri_xhair.m @@ -14,79 +14,78 @@ function xhair = rri_xhair(varargin) - if nargin == 0 - error('Please enter a point position as first argument'); - return; - end - - if nargin > 0 - p = varargin{1}; - - if ~isnumeric(p) | length(p) ~= 2 - error('Invalid point position'); - return; - else - xhair = []; - end - end - - if nargin > 1 - xhair = varargin{2}; - - if ~isempty(xhair) - if ~isstruct(xhair) - error('Invalid xhair struct'); - return; - elseif ~isfield(xhair,'lx') | ~isfield(xhair,'ly') - error('Invalid xhair struct'); - return; - elseif ~ishandle(xhair.lx) | ~ishandle(xhair.ly) - error('Invalid xhair struct'); - return; - end - - lx = xhair.lx; - ly = xhair.ly; - else - lx = []; - ly = []; - end - end - - if nargin > 2 - h_ax = varargin{3}; - - if ~ishandle(h_ax) - error('Invalid axes handle'); - return; - elseif ~strcmp(lower(get(h_ax,'type')), 'axes') - error('Invalid axes handle'); - return; + if nargin == 0 + error('Please enter a point position as first argument'); + return + end + + if nargin > 0 + p = varargin{1}; + + if ~isnumeric(p) | length(p) ~= 2 + error('Invalid point position'); + return + else + xhair = []; + end + end + + if nargin > 1 + xhair = varargin{2}; + + if ~isempty(xhair) + if ~isstruct(xhair) + error('Invalid xhair struct'); + return + elseif ~isfield(xhair, 'lx') | ~isfield(xhair, 'ly') + error('Invalid xhair struct'); + return + elseif ~ishandle(xhair.lx) | ~ishandle(xhair.ly) + error('Invalid xhair struct'); + return end - else - h_ax = gca; - end - - x_range = get(h_ax,'xlim'); - y_range = get(h_ax,'ylim'); - - if ~isempty(xhair) - set(lx, 'ydata', [p(2) p(2)]); - set(ly, 'xdata', [p(1) p(1)]); - set(h_ax, 'selected', 'on'); - set(h_ax, 'selected', 'off'); - else - figure(get(h_ax,'parent')); - axes(h_ax); - - xhair.lx = line('xdata', x_range, 'ydata', [p(2) p(2)], ... - 'zdata', [11 11], 'color', [1 0 0], 'hittest', 'off'); - xhair.ly = line('xdata', [p(1) p(1)], 'ydata', y_range, ... - 'zdata', [11 11], 'color', [1 0 0], 'hittest', 'off'); - end - - set(h_ax,'xlim',x_range); - set(h_ax,'ylim',y_range); - - return; + lx = xhair.lx; + ly = xhair.ly; + else + lx = []; + ly = []; + end + end + + if nargin > 2 + h_ax = varargin{3}; + + if ~ishandle(h_ax) + error('Invalid axes handle'); + return + elseif ~strcmp(lower(get(h_ax, 'type')), 'axes') + error('Invalid axes handle'); + return + end + else + h_ax = gca; + end + + x_range = get(h_ax, 'xlim'); + y_range = get(h_ax, 'ylim'); + + if ~isempty(xhair) + set(lx, 'ydata', [p(2) p(2)]); + set(ly, 'xdata', [p(1) p(1)]); + set(h_ax, 'selected', 'on'); + set(h_ax, 'selected', 'off'); + else + figure(get(h_ax, 'parent')); + axes(h_ax); + + xhair.lx = line('xdata', x_range, 'ydata', [p(2) p(2)], ... + 'zdata', [11 11], 'color', [1 0 0], 'hittest', 'off'); + xhair.ly = line('xdata', [p(1) p(1)], 'ydata', y_range, ... + 'zdata', [11 11], 'color', [1 0 0], 'hittest', 'off'); + end + + set(h_ax, 'xlim', x_range); + set(h_ax, 'ylim', y_range); + + return diff --git a/lib/NiftiTools/rri_zoom_menu.m b/lib/NiftiTools/rri_zoom_menu.m index e1c9c6ce..8d3c4287 100644 --- a/lib/NiftiTools/rri_zoom_menu.m +++ b/lib/NiftiTools/rri_zoom_menu.m @@ -5,29 +5,28 @@ % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % -%-------------------------------------------------------------------- +% -------------------------------------------------------------------- function menu_hdl = rri_zoom_menu(fig) - if isnumeric(fig) - menu_hdl = uimenu('Parent',fig, ... - 'Label','Zoom on', ... - 'Userdata', 1, ... - 'Callback','rri_zoom_menu(''zoom'');'); + if isnumeric(fig) + menu_hdl = uimenu('Parent', fig, ... + 'Label', 'Zoom on', ... + 'Userdata', 1, ... + 'Callback', 'rri_zoom_menu(''zoom'');'); - return; - end + return + end - zoom_on_state = get(gcbo,'Userdata'); + zoom_on_state = get(gcbo, 'Userdata'); - if (zoom_on_state == 1) - zoom on; - set(gcbo,'Userdata',0,'Label','Zoom off'); - set(gcbf,'pointer','crosshair'); - else - zoom off; - set(gcbo,'Userdata',1,'Label','Zoom on'); - set(gcbf,'pointer','arrow'); - end - - return % rri_zoom_menu + if zoom_on_state == 1 + zoom on; + set(gcbo, 'Userdata', 0, 'Label', 'Zoom off'); + set(gcbf, 'pointer', 'crosshair'); + else + zoom off; + set(gcbo, 'Userdata', 1, 'Label', 'Zoom on'); + set(gcbf, 'pointer', 'arrow'); + end + return % rri_zoom_menu diff --git a/lib/NiftiTools/save_nii.m b/lib/NiftiTools/save_nii.m index b2d0dd44..38721b43 100644 --- a/lib/NiftiTools/save_nii.m +++ b/lib/NiftiTools/save_nii.m @@ -1,286 +1,299 @@ % Save NIFTI dataset. Support both *.nii and *.hdr/*.img file extension. % If file extension is not provided, *.hdr/*.img will be used as default. -% +% % Usage: save_nii(nii, filename, [old_RGB]) -% +% % nii.hdr - struct with NIFTI header fields (from load_nii.m or make_nii.m) % % nii.img - 3D (or 4D) matrix of NIFTI data. % % filename - NIFTI file name. % -% old_RGB - an optional boolean variable to handle special RGB data -% sequence [R1 R2 ... G1 G2 ... B1 B2 ...] that is used only by +% old_RGB - an optional boolean variable to handle special RGB data +% sequence [R1 R2 ... G1 G2 ... B1 B2 ...] that is used only by % AnalyzeDirect (Analyze Software). Since both NIfTI and Analyze % file format use RGB triple [R1 G1 B1 R2 G2 B2 ...] sequentially % for each voxel, this variable is set to FALSE by default. If you -% would like the saved image only to be opened by AnalyzeDirect +% would like the saved image only to be opened by AnalyzeDirect % Software, set old_RGB to TRUE (or 1). It will be set to 0, if it % is default or empty. -% +% % Tip: to change the data type, set nii.hdr.dime.datatype, -% and nii.hdr.dime.bitpix to: -% -% 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN -% 1 Binary (ubit1, bitpix=1) % DT_BINARY -% 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 -% 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 -% 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 -% 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 +% and nii.hdr.dime.bitpix to: +% +% 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN +% 1 Binary (ubit1, bitpix=1) % DT_BINARY +% 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 +% 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 +% 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 +% 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 -% 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 -% 128 uint RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 -% 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 +% 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 +% 128 uint RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 +% 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 -% 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 -% 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 +% 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 +% 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 -% 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 -% 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 -% 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 -% 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 -% +% 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 +% 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 +% 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 +% 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 +% % Part of this file is copied and modified from: % http://www.mathworks.com/matlabcentral/fileexchange/1878-mri-analyze-tools % % NIFTI data format can be found on: http://nifti.nimh.nih.gov % % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% - "old_RGB" related codes in "save_nii.m" are added by Mike Harms (2006.06.28) +% - "old_RGB" related codes in "save_nii.m" are added by Mike Harms (2006.06.28) % function save_nii(nii, fileprefix, old_RGB) - - if ~exist('nii','var') | isempty(nii) | ~isfield(nii,'hdr') | ... - ~isfield(nii,'img') | ~exist('fileprefix','var') | isempty(fileprefix) - - error('Usage: save_nii(nii, filename, [old_RGB])'); - end - - if isfield(nii,'untouch') & nii.untouch == 1 - error('Usage: please use ''save_untouch_nii.m'' for the untouched structure.'); - end - - if ~exist('old_RGB','var') | isempty(old_RGB) - old_RGB = 0; - end - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(fileprefix) > 2 & strcmp(fileprefix(end-2:end), '.gz') - - if ~strcmp(fileprefix(end-6:end), '.img.gz') & ... - ~strcmp(fileprefix(end-6:end), '.hdr.gz') & ... - ~strcmp(fileprefix(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - else - gzFile = 1; - fileprefix = fileprefix(1:end-3); - end - end - - filetype = 1; - - % Note: fileprefix is actually the filename you want to save - % - if findstr('.nii',fileprefix) & strcmp(fileprefix(end-3:end), '.nii') - filetype = 2; - fileprefix(end-3:end)=''; - end - - if findstr('.hdr',fileprefix) & strcmp(fileprefix(end-3:end), '.hdr') - fileprefix(end-3:end)=''; - end - - if findstr('.img',fileprefix) & strcmp(fileprefix(end-3:end), '.img') - fileprefix(end-3:end)=''; - end - - write_nii(nii, filetype, fileprefix, old_RGB); - - % gzip output file if requested - % - if exist('gzFile', 'var') - if filetype == 1 - gzip([fileprefix, '.img']); - delete([fileprefix, '.img']); - gzip([fileprefix, '.hdr']); - delete([fileprefix, '.hdr']); - elseif filetype == 2 - gzip([fileprefix, '.nii']); - delete([fileprefix, '.nii']); - end; - end; - - if filetype == 1 - - % So earlier versions of SPM can also open it with correct originator - % - M=[[diag(nii.hdr.dime.pixdim(2:4)) -[nii.hdr.hist.originator(1:3).*nii.hdr.dime.pixdim(2:4)]'];[0 0 0 1]]; - save([fileprefix '.mat'], 'M'); - end - - return % save_nii - - -%----------------------------------------------------------------------------------- + + if ~exist('nii', 'var') | isempty(nii) | ~isfield(nii, 'hdr') | ... + ~isfield(nii, 'img') | ~exist('fileprefix', 'var') | isempty(fileprefix) + + error('Usage: save_nii(nii, filename, [old_RGB])'); + end + + if isfield(nii, 'untouch') & nii.untouch == 1 + error('Usage: please use ''save_untouch_nii.m'' for the untouched structure.'); + end + + if ~exist('old_RGB', 'var') | isempty(old_RGB) + old_RGB = 0; + end + + v = version; + + % Check file extension. If .gz, unpack it into temp folder + % + if length(fileprefix) > 2 & strcmp(fileprefix(end - 2:end), '.gz') + + if ~strcmp(fileprefix(end - 6:end), '.img.gz') & ... + ~strcmp(fileprefix(end - 6:end), '.hdr.gz') & ... + ~strcmp(fileprefix(end - 6:end), '.nii.gz') + + error('Please check filename.'); + end + + if str2num(v(1:3)) < 7.1 | ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + else + gzFile = 1; + fileprefix = fileprefix(1:end - 3); + end + end + + filetype = 1; + + % Note: fileprefix is actually the filename you want to save + % + if findstr('.nii', fileprefix) & strcmp(fileprefix(end - 3:end), '.nii') + filetype = 2; + fileprefix(end - 3:end) = ''; + end + + if findstr('.hdr', fileprefix) & strcmp(fileprefix(end - 3:end), '.hdr') + fileprefix(end - 3:end) = ''; + end + + if findstr('.img', fileprefix) & strcmp(fileprefix(end - 3:end), '.img') + fileprefix(end - 3:end) = ''; + end + + write_nii(nii, filetype, fileprefix, old_RGB); + + % gzip output file if requested + % + if exist('gzFile', 'var') + if filetype == 1 + gzip([fileprefix, '.img']); + delete([fileprefix, '.img']); + gzip([fileprefix, '.hdr']); + delete([fileprefix, '.hdr']); + elseif filetype == 2 + gzip([fileprefix, '.nii']); + delete([fileprefix, '.nii']); + end + end + + if filetype == 1 + + % So earlier versions of SPM can also open it with correct originator + % + M = [[diag(nii.hdr.dime.pixdim(2:4)) - [nii.hdr.hist.originator(1:3) .* nii.hdr.dime.pixdim(2:4)]']; [0 0 0 1]]; + save([fileprefix '.mat'], 'M'); + end + + return % save_nii + + % ----------------------------------------------------------------------------------- function write_nii(nii, filetype, fileprefix, old_RGB) - hdr = nii.hdr; - - if isfield(nii,'ext') & ~isempty(nii.ext) - ext = nii.ext; - [ext, esize_total] = verify_nii_ext(ext); - else - ext = []; - end - - switch double(hdr.dime.datatype), - case 1, - hdr.dime.bitpix = int16(1 ); precision = 'ubit1'; - case 2, - hdr.dime.bitpix = int16(8 ); precision = 'uint8'; - case 4, - hdr.dime.bitpix = int16(16); precision = 'int16'; - case 8, - hdr.dime.bitpix = int16(32); precision = 'int32'; - case 16, - hdr.dime.bitpix = int16(32); precision = 'float32'; - case 32, - hdr.dime.bitpix = int16(64); precision = 'float32'; - case 64, - hdr.dime.bitpix = int16(64); precision = 'float64'; - case 128, - hdr.dime.bitpix = int16(24); precision = 'uint8'; - case 256 - hdr.dime.bitpix = int16(8 ); precision = 'int8'; - case 511, - hdr.dime.bitpix = int16(96); precision = 'float32'; - case 512 - hdr.dime.bitpix = int16(16); precision = 'uint16'; - case 768 - hdr.dime.bitpix = int16(32); precision = 'uint32'; - case 1024 - hdr.dime.bitpix = int16(64); precision = 'int64'; - case 1280 - hdr.dime.bitpix = int16(64); precision = 'uint64'; - case 1792, - hdr.dime.bitpix = int16(128); precision = 'float64'; - otherwise + hdr = nii.hdr; + + if isfield(nii, 'ext') & ~isempty(nii.ext) + ext = nii.ext; + [ext, esize_total] = verify_nii_ext(ext); + else + ext = []; + end + + switch double(hdr.dime.datatype) + case 1 + hdr.dime.bitpix = int16(1); + precision = 'ubit1'; + case 2 + hdr.dime.bitpix = int16(8); + precision = 'uint8'; + case 4 + hdr.dime.bitpix = int16(16); + precision = 'int16'; + case 8 + hdr.dime.bitpix = int16(32); + precision = 'int32'; + case 16 + hdr.dime.bitpix = int16(32); + precision = 'float32'; + case 32 + hdr.dime.bitpix = int16(64); + precision = 'float32'; + case 64 + hdr.dime.bitpix = int16(64); + precision = 'float64'; + case 128 + hdr.dime.bitpix = int16(24); + precision = 'uint8'; + case 256 + hdr.dime.bitpix = int16(8); + precision = 'int8'; + case 511 + hdr.dime.bitpix = int16(96); + precision = 'float32'; + case 512 + hdr.dime.bitpix = int16(16); + precision = 'uint16'; + case 768 + hdr.dime.bitpix = int16(32); + precision = 'uint32'; + case 1024 + hdr.dime.bitpix = int16(64); + precision = 'int64'; + case 1280 + hdr.dime.bitpix = int16(64); + precision = 'uint64'; + case 1792 + hdr.dime.bitpix = int16(128); + precision = 'float64'; + otherwise error('This datatype is not supported'); - end - - hdr.dime.glmax = round(double(max(nii.img(:)))); - hdr.dime.glmin = round(double(min(nii.img(:)))); - - if filetype == 2 - fid = fopen(sprintf('%s.nii',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.nii.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 352; - - if ~isempty(ext) - hdr.dime.vox_offset = hdr.dime.vox_offset + esize_total; - end - - hdr.hist.magic = 'n+1'; - save_nii_hdr(hdr, fid); - - if ~isempty(ext) - save_nii_ext(ext, fid); - end - else - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 0; - hdr.hist.magic = 'ni1'; - save_nii_hdr(hdr, fid); - - if ~isempty(ext) - save_nii_ext(ext, fid); - end - - fclose(fid); - fid = fopen(sprintf('%s.img',fileprefix),'w'); - end - - ScanDim = double(hdr.dime.dim(5)); % t - SliceDim = double(hdr.dime.dim(4)); % z - RowDim = double(hdr.dime.dim(3)); % y - PixelDim = double(hdr.dime.dim(2)); % x - SliceSz = double(hdr.dime.pixdim(4)); - RowSz = double(hdr.dime.pixdim(3)); - PixelSz = double(hdr.dime.pixdim(2)); - - x = 1:PixelDim; - - if filetype == 2 & isempty(ext) - skip_bytes = double(hdr.dime.vox_offset) - 348; - else - skip_bytes = 0; - end - - if double(hdr.dime.datatype) == 128 - - % RGB planes are expected to be in the 4th dimension of nii.img - % - if(size(nii.img,4)~=3) - error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); - end - - if old_RGB - nii.img = permute(nii.img, [1 2 4 3 5 6 7 8]); - else - nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); - end - end - - if double(hdr.dime.datatype) == 511 - - % RGB planes are expected to be in the 4th dimension of nii.img - % - if(size(nii.img,4)~=3) - error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); - end - - if old_RGB - nii.img = permute(nii.img, [1 2 4 3 5 6 7 8]); - else - nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); - end - end - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - real_img = real(nii.img(:))'; - nii.img = imag(nii.img(:))'; - nii.img = [real_img; nii.img]; - end - - if skip_bytes - fwrite(fid, zeros(1,skip_bytes), 'uint8'); - end - - fwrite(fid, nii.img, precision); -% fwrite(fid, nii.img, precision, skip_bytes); % error using skip - fclose(fid); - - return; % write_nii - + end + + hdr.dime.glmax = round(double(max(nii.img(:)))); + hdr.dime.glmin = round(double(min(nii.img(:)))); + + if filetype == 2 + fid = fopen(sprintf('%s.nii', fileprefix), 'w'); + + if fid < 0 + msg = sprintf('Cannot open file %s.nii.', fileprefix); + error(msg); + end + + hdr.dime.vox_offset = 352; + + if ~isempty(ext) + hdr.dime.vox_offset = hdr.dime.vox_offset + esize_total; + end + + hdr.hist.magic = 'n+1'; + save_nii_hdr(hdr, fid); + + if ~isempty(ext) + save_nii_ext(ext, fid); + end + else + fid = fopen(sprintf('%s.hdr', fileprefix), 'w'); + + if fid < 0 + msg = sprintf('Cannot open file %s.hdr.', fileprefix); + error(msg); + end + + hdr.dime.vox_offset = 0; + hdr.hist.magic = 'ni1'; + save_nii_hdr(hdr, fid); + + if ~isempty(ext) + save_nii_ext(ext, fid); + end + + fclose(fid); + fid = fopen(sprintf('%s.img', fileprefix), 'w'); + end + + ScanDim = double(hdr.dime.dim(5)); % t + SliceDim = double(hdr.dime.dim(4)); % z + RowDim = double(hdr.dime.dim(3)); % y + PixelDim = double(hdr.dime.dim(2)); % x + SliceSz = double(hdr.dime.pixdim(4)); + RowSz = double(hdr.dime.pixdim(3)); + PixelSz = double(hdr.dime.pixdim(2)); + + x = 1:PixelDim; + + if filetype == 2 & isempty(ext) + skip_bytes = double(hdr.dime.vox_offset) - 348; + else + skip_bytes = 0; + end + + if double(hdr.dime.datatype) == 128 + + % RGB planes are expected to be in the 4th dimension of nii.img + % + if size(nii.img, 4) ~= 3 + error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); + end + + if old_RGB + nii.img = permute(nii.img, [1 2 4 3 5 6 7 8]); + else + nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); + end + end + + if double(hdr.dime.datatype) == 511 + + % RGB planes are expected to be in the 4th dimension of nii.img + % + if size(nii.img, 4) ~= 3 + error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); + end + + if old_RGB + nii.img = permute(nii.img, [1 2 4 3 5 6 7 8]); + else + nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); + end + end + + % For complex float32 or complex float64, voxel values + % include [real, imag] + % + if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 + real_img = real(nii.img(:))'; + nii.img = imag(nii.img(:))'; + nii.img = [real_img; nii.img]; + end + + if skip_bytes + fwrite(fid, zeros(1, skip_bytes), 'uint8'); + end + + fwrite(fid, nii.img, precision); + % fwrite(fid, nii.img, precision, skip_bytes); % error using skip + fclose(fid); + + return % write_nii diff --git a/lib/NiftiTools/save_nii_ext.m b/lib/NiftiTools/save_nii_ext.m index be5f196b..56ca38df 100644 --- a/lib/NiftiTools/save_nii_ext.m +++ b/lib/NiftiTools/save_nii_ext.m @@ -10,29 +10,27 @@ % function save_nii_ext(ext, fid) - if ~exist('ext','var') || ~exist('fid','var') - error('Usage: save_nii_ext(ext, fid)'); - end + if ~exist('ext', 'var') || ~exist('fid', 'var') + error('Usage: save_nii_ext(ext, fid)'); + end - if ~isfield(ext,'extension') || ~isfield(ext,'section') || ~isfield(ext,'num_ext') - error('Wrong header extension'); - end + if ~isfield(ext, 'extension') || ~isfield(ext, 'section') || ~isfield(ext, 'num_ext') + error('Wrong header extension'); + end - write_ext(ext, fid); + write_ext(ext, fid); - return; % save_nii_ext + return % save_nii_ext - -%--------------------------------------------------------------------- + % --------------------------------------------------------------------- function write_ext(ext, fid) - fwrite(fid, ext.extension, 'uchar'); - - for i=1:ext.num_ext - fwrite(fid, ext.section(i).esize, 'int32'); - fwrite(fid, ext.section(i).ecode, 'int32'); - fwrite(fid, ext.section(i).edata, 'uchar'); - end + fwrite(fid, ext.extension, 'uchar'); - return; % write_ext + for i = 1:ext.num_ext + fwrite(fid, ext.section(i).esize, 'int32'); + fwrite(fid, ext.section(i).ecode, 'int32'); + fwrite(fid, ext.section(i).edata, 'uchar'); + end + return % write_ext diff --git a/lib/NiftiTools/save_nii_hdr.m b/lib/NiftiTools/save_nii_hdr.m index 5d7f7ddc..d1a10135 100644 --- a/lib/NiftiTools/save_nii_hdr.m +++ b/lib/NiftiTools/save_nii_hdr.m @@ -4,223 +4,219 @@ function save_nii_hdr(hdr, fid) - if ~exist('hdr','var') || ~exist('fid','var') - error('Usage: save_nii_hdr(hdr, fid)'); - end - - if ~isequal(hdr.hk.sizeof_hdr,348), - error('hdr.hk.sizeof_hdr must be 348.'); - end - - if hdr.hist.qform_code == 0 && hdr.hist.sform_code == 0 - hdr.hist.sform_code = 1; - hdr.hist.srow_x(1) = hdr.dime.pixdim(2); - hdr.hist.srow_x(2) = 0; - hdr.hist.srow_x(3) = 0; - hdr.hist.srow_y(1) = 0; - hdr.hist.srow_y(2) = hdr.dime.pixdim(3); - hdr.hist.srow_y(3) = 0; - hdr.hist.srow_z(1) = 0; - hdr.hist.srow_z(2) = 0; - hdr.hist.srow_z(3) = hdr.dime.pixdim(4); - hdr.hist.srow_x(4) = (1-hdr.hist.originator(1))*hdr.dime.pixdim(2); - hdr.hist.srow_y(4) = (1-hdr.hist.originator(2))*hdr.dime.pixdim(3); - hdr.hist.srow_z(4) = (1-hdr.hist.originator(3))*hdr.dime.pixdim(4); - end - - write_header(hdr, fid); - - return; % save_nii_hdr - - -%--------------------------------------------------------------------- + if ~exist('hdr', 'var') || ~exist('fid', 'var') + error('Usage: save_nii_hdr(hdr, fid)'); + end + + if ~isequal(hdr.hk.sizeof_hdr, 348) + error('hdr.hk.sizeof_hdr must be 348.'); + end + + if hdr.hist.qform_code == 0 && hdr.hist.sform_code == 0 + hdr.hist.sform_code = 1; + hdr.hist.srow_x(1) = hdr.dime.pixdim(2); + hdr.hist.srow_x(2) = 0; + hdr.hist.srow_x(3) = 0; + hdr.hist.srow_y(1) = 0; + hdr.hist.srow_y(2) = hdr.dime.pixdim(3); + hdr.hist.srow_y(3) = 0; + hdr.hist.srow_z(1) = 0; + hdr.hist.srow_z(2) = 0; + hdr.hist.srow_z(3) = hdr.dime.pixdim(4); + hdr.hist.srow_x(4) = (1 - hdr.hist.originator(1)) * hdr.dime.pixdim(2); + hdr.hist.srow_y(4) = (1 - hdr.hist.originator(2)) * hdr.dime.pixdim(3); + hdr.hist.srow_z(4) = (1 - hdr.hist.originator(3)) * hdr.dime.pixdim(4); + end + + write_header(hdr, fid); + + return % save_nii_hdr + + % --------------------------------------------------------------------- function write_header(hdr, fid) - % Original header structures - % struct dsr /* dsr = hdr */ - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ + % Original header structures + % struct dsr /* dsr = hdr */ + % { + % struct header_key hk; /* 0 + 40 */ + % struct image_dimension dime; /* 40 + 108 */ + % struct data_history hist; /* 148 + 200 */ + % }; /* total= 348 bytes*/ - header_key(fid, hdr.hk); - image_dimension(fid, hdr.dime); - data_history(fid, hdr.hist); + header_key(fid, hdr.hk); + image_dimension(fid, hdr.dime); + data_history(fid, hdr.hist); - % check the file size is 348 bytes - % - fbytes = ftell(fid); + % check the file size is 348 bytes + % + fbytes = ftell(fid); - if ~isequal(fbytes,348), - msg = sprintf('Header size is not 348 bytes.'); - warning(msg); - end + if ~isequal(fbytes, 348) + msg = sprintf('Header size is not 348 bytes.'); + warning(msg); + end - return; % write_header + return % write_header - -%--------------------------------------------------------------------- + % --------------------------------------------------------------------- function header_key(fid, hk) - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char dim_info; % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - - fwrite(fid, hk.sizeof_hdr(1), 'int32'); % must be 348. - - % data_type = sprintf('%-10s',hk.data_type); % ensure it is 10 chars from left - % fwrite(fid, data_type(1:10), 'uchar'); - pad = zeros(1, 10-length(hk.data_type)); - hk.data_type = [hk.data_type char(pad)]; - fwrite(fid, hk.data_type(1:10), 'uchar'); - - % db_name = sprintf('%-18s', hk.db_name); % ensure it is 18 chars from left - % fwrite(fid, db_name(1:18), 'uchar'); - pad = zeros(1, 18-length(hk.db_name)); - hk.db_name = [hk.db_name char(pad)]; - fwrite(fid, hk.db_name(1:18), 'uchar'); - - fwrite(fid, hk.extents(1), 'int32'); - fwrite(fid, hk.session_error(1), 'int16'); - fwrite(fid, hk.regular(1), 'uchar'); % might be uint8 - - % fwrite(fid, hk.hkey_un0(1), 'uchar'); - % fwrite(fid, hk.hkey_un0(1), 'uint8'); - fwrite(fid, hk.dim_info(1), 'uchar'); - - return; % header_key - - -%--------------------------------------------------------------------- + fseek(fid, 0, 'bof'); + + % Original header structures + % struct header_key /* header key */ + % { /* off + size */ + % int sizeof_hdr /* 0 + 4 */ + % char data_type[10]; /* 4 + 10 */ + % char db_name[18]; /* 14 + 18 */ + % int extents; /* 32 + 4 */ + % short int session_error; /* 36 + 2 */ + % char regular; /* 38 + 1 */ + % char dim_info; % char hkey_un0; /* 39 + 1 */ + % }; /* total=40 bytes */ + + fwrite(fid, hk.sizeof_hdr(1), 'int32'); % must be 348. + + % data_type = sprintf('%-10s',hk.data_type); % ensure it is 10 chars from left + % fwrite(fid, data_type(1:10), 'uchar'); + pad = zeros(1, 10 - length(hk.data_type)); + hk.data_type = [hk.data_type char(pad)]; + fwrite(fid, hk.data_type(1:10), 'uchar'); + + % db_name = sprintf('%-18s', hk.db_name); % ensure it is 18 chars from left + % fwrite(fid, db_name(1:18), 'uchar'); + pad = zeros(1, 18 - length(hk.db_name)); + hk.db_name = [hk.db_name char(pad)]; + fwrite(fid, hk.db_name(1:18), 'uchar'); + + fwrite(fid, hk.extents(1), 'int32'); + fwrite(fid, hk.session_error(1), 'int16'); + fwrite(fid, hk.regular(1), 'uchar'); % might be uint8 + + % fwrite(fid, hk.hkey_un0(1), 'uchar'); + % fwrite(fid, hk.hkey_un0(1), 'uint8'); + fwrite(fid, hk.dim_info(1), 'uchar'); + + return % header_key + + % --------------------------------------------------------------------- function image_dimension(fid, dime) - % Original header structures - % struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % float intent_p1; % char vox_units[4]; /* 16 + 4 */ - % float intent_p2; % char cal_units[8]; /* 20 + 4 */ - % float intent_p3; % char cal_units[8]; /* 24 + 4 */ - % short int intent_code; % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int slice_start; % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width - % pixdim[2] - voxel height - % pixdim[3] - interslice distance - % pixdim[4] - volume timing, in msec - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float scl_slope; % float roi_scale; /* 72 + 4 */ - % float scl_inter; % float funused1; /* 76 + 4 */ - % short slice_end; % float funused2; /* 80 + 2 */ - % char slice_code; % float funused2; /* 82 + 1 */ - % char xyzt_units; % float funused2; /* 83 + 1 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % float slice_duration; % int compressed; /* 92 + 4 */ - % float toffset; % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - fwrite(fid, dime.dim(1:8), 'int16'); - fwrite(fid, dime.intent_p1(1), 'float32'); - fwrite(fid, dime.intent_p2(1), 'float32'); - fwrite(fid, dime.intent_p3(1), 'float32'); - fwrite(fid, dime.intent_code(1), 'int16'); - fwrite(fid, dime.datatype(1), 'int16'); - fwrite(fid, dime.bitpix(1), 'int16'); - fwrite(fid, dime.slice_start(1), 'int16'); - fwrite(fid, dime.pixdim(1:8), 'float32'); - fwrite(fid, dime.vox_offset(1), 'float32'); - fwrite(fid, dime.scl_slope(1), 'float32'); - fwrite(fid, dime.scl_inter(1), 'float32'); - fwrite(fid, dime.slice_end(1), 'int16'); - fwrite(fid, dime.slice_code(1), 'uchar'); - fwrite(fid, dime.xyzt_units(1), 'uchar'); - fwrite(fid, dime.cal_max(1), 'float32'); - fwrite(fid, dime.cal_min(1), 'float32'); - fwrite(fid, dime.slice_duration(1), 'float32'); - fwrite(fid, dime.toffset(1), 'float32'); - fwrite(fid, dime.glmax(1), 'int32'); - fwrite(fid, dime.glmin(1), 'int32'); - - return; % image_dimension - - -%--------------------------------------------------------------------- + % Original header structures + % struct image_dimension + % { /* off + size */ + % short int dim[8]; /* 0 + 16 */ + % float intent_p1; % char vox_units[4]; /* 16 + 4 */ + % float intent_p2; % char cal_units[8]; /* 20 + 4 */ + % float intent_p3; % char cal_units[8]; /* 24 + 4 */ + % short int intent_code; % short int unused1; /* 28 + 2 */ + % short int datatype; /* 30 + 2 */ + % short int bitpix; /* 32 + 2 */ + % short int slice_start; % short int dim_un0; /* 34 + 2 */ + % float pixdim[8]; /* 36 + 32 */ + % /* + % pixdim[] specifies the voxel dimensions: + % pixdim[1] - voxel width + % pixdim[2] - voxel height + % pixdim[3] - interslice distance + % pixdim[4] - volume timing, in msec + % ..etc + % */ + % float vox_offset; /* 68 + 4 */ + % float scl_slope; % float roi_scale; /* 72 + 4 */ + % float scl_inter; % float funused1; /* 76 + 4 */ + % short slice_end; % float funused2; /* 80 + 2 */ + % char slice_code; % float funused2; /* 82 + 1 */ + % char xyzt_units; % float funused2; /* 83 + 1 */ + % float cal_max; /* 84 + 4 */ + % float cal_min; /* 88 + 4 */ + % float slice_duration; % int compressed; /* 92 + 4 */ + % float toffset; % int verified; /* 96 + 4 */ + % int glmax; /* 100 + 4 */ + % int glmin; /* 104 + 4 */ + % }; /* total=108 bytes */ + + fwrite(fid, dime.dim(1:8), 'int16'); + fwrite(fid, dime.intent_p1(1), 'float32'); + fwrite(fid, dime.intent_p2(1), 'float32'); + fwrite(fid, dime.intent_p3(1), 'float32'); + fwrite(fid, dime.intent_code(1), 'int16'); + fwrite(fid, dime.datatype(1), 'int16'); + fwrite(fid, dime.bitpix(1), 'int16'); + fwrite(fid, dime.slice_start(1), 'int16'); + fwrite(fid, dime.pixdim(1:8), 'float32'); + fwrite(fid, dime.vox_offset(1), 'float32'); + fwrite(fid, dime.scl_slope(1), 'float32'); + fwrite(fid, dime.scl_inter(1), 'float32'); + fwrite(fid, dime.slice_end(1), 'int16'); + fwrite(fid, dime.slice_code(1), 'uchar'); + fwrite(fid, dime.xyzt_units(1), 'uchar'); + fwrite(fid, dime.cal_max(1), 'float32'); + fwrite(fid, dime.cal_min(1), 'float32'); + fwrite(fid, dime.slice_duration(1), 'float32'); + fwrite(fid, dime.toffset(1), 'float32'); + fwrite(fid, dime.glmax(1), 'int32'); + fwrite(fid, dime.glmin(1), 'int32'); + + return % image_dimension + + % --------------------------------------------------------------------- function data_history(fid, hist) - % Original header structures - %struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % short int qform_code; /* 104 + 2 */ - % short int sform_code; /* 106 + 2 */ - % float quatern_b; /* 108 + 4 */ - % float quatern_c; /* 112 + 4 */ - % float quatern_d; /* 116 + 4 */ - % float qoffset_x; /* 120 + 4 */ - % float qoffset_y; /* 124 + 4 */ - % float qoffset_z; /* 128 + 4 */ - % float srow_x[4]; /* 132 + 16 */ - % float srow_y[4]; /* 148 + 16 */ - % float srow_z[4]; /* 164 + 16 */ - % char intent_name[16]; /* 180 + 16 */ - % char magic[4]; % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - % descrip = sprintf('%-80s', hist.descrip); % 80 chars from left - % fwrite(fid, descrip(1:80), 'uchar'); - pad = zeros(1, 80-length(hist.descrip)); - hist.descrip = [hist.descrip char(pad)]; - fwrite(fid, hist.descrip(1:80), 'uchar'); - - % aux_file = sprintf('%-24s', hist.aux_file); % 24 chars from left - % fwrite(fid, aux_file(1:24), 'uchar'); - pad = zeros(1, 24-length(hist.aux_file)); - hist.aux_file = [hist.aux_file char(pad)]; - fwrite(fid, hist.aux_file(1:24), 'uchar'); - - fwrite(fid, hist.qform_code, 'int16'); - fwrite(fid, hist.sform_code, 'int16'); - fwrite(fid, hist.quatern_b, 'float32'); - fwrite(fid, hist.quatern_c, 'float32'); - fwrite(fid, hist.quatern_d, 'float32'); - fwrite(fid, hist.qoffset_x, 'float32'); - fwrite(fid, hist.qoffset_y, 'float32'); - fwrite(fid, hist.qoffset_z, 'float32'); - fwrite(fid, hist.srow_x(1:4), 'float32'); - fwrite(fid, hist.srow_y(1:4), 'float32'); - fwrite(fid, hist.srow_z(1:4), 'float32'); - - % intent_name = sprintf('%-16s', hist.intent_name); % 16 chars from left - % fwrite(fid, intent_name(1:16), 'uchar'); - pad = zeros(1, 16-length(hist.intent_name)); - hist.intent_name = [hist.intent_name char(pad)]; - fwrite(fid, hist.intent_name(1:16), 'uchar'); - - % magic = sprintf('%-4s', hist.magic); % 4 chars from left - % fwrite(fid, magic(1:4), 'uchar'); - pad = zeros(1, 4-length(hist.magic)); - hist.magic = [hist.magic char(pad)]; - fwrite(fid, hist.magic(1:4), 'uchar'); - - return; % data_history + % Original header structures + % struct data_history + % { /* off + size */ + % char descrip[80]; /* 0 + 80 */ + % char aux_file[24]; /* 80 + 24 */ + % short int qform_code; /* 104 + 2 */ + % short int sform_code; /* 106 + 2 */ + % float quatern_b; /* 108 + 4 */ + % float quatern_c; /* 112 + 4 */ + % float quatern_d; /* 116 + 4 */ + % float qoffset_x; /* 120 + 4 */ + % float qoffset_y; /* 124 + 4 */ + % float qoffset_z; /* 128 + 4 */ + % float srow_x[4]; /* 132 + 16 */ + % float srow_y[4]; /* 148 + 16 */ + % float srow_z[4]; /* 164 + 16 */ + % char intent_name[16]; /* 180 + 16 */ + % char magic[4]; % int smin; /* 196 + 4 */ + % }; /* total=200 bytes */ + + % descrip = sprintf('%-80s', hist.descrip); % 80 chars from left + % fwrite(fid, descrip(1:80), 'uchar'); + pad = zeros(1, 80 - length(hist.descrip)); + hist.descrip = [hist.descrip char(pad)]; + fwrite(fid, hist.descrip(1:80), 'uchar'); + + % aux_file = sprintf('%-24s', hist.aux_file); % 24 chars from left + % fwrite(fid, aux_file(1:24), 'uchar'); + pad = zeros(1, 24 - length(hist.aux_file)); + hist.aux_file = [hist.aux_file char(pad)]; + fwrite(fid, hist.aux_file(1:24), 'uchar'); + + fwrite(fid, hist.qform_code, 'int16'); + fwrite(fid, hist.sform_code, 'int16'); + fwrite(fid, hist.quatern_b, 'float32'); + fwrite(fid, hist.quatern_c, 'float32'); + fwrite(fid, hist.quatern_d, 'float32'); + fwrite(fid, hist.qoffset_x, 'float32'); + fwrite(fid, hist.qoffset_y, 'float32'); + fwrite(fid, hist.qoffset_z, 'float32'); + fwrite(fid, hist.srow_x(1:4), 'float32'); + fwrite(fid, hist.srow_y(1:4), 'float32'); + fwrite(fid, hist.srow_z(1:4), 'float32'); + + % intent_name = sprintf('%-16s', hist.intent_name); % 16 chars from left + % fwrite(fid, intent_name(1:16), 'uchar'); + pad = zeros(1, 16 - length(hist.intent_name)); + hist.intent_name = [hist.intent_name char(pad)]; + fwrite(fid, hist.intent_name(1:16), 'uchar'); + + % magic = sprintf('%-4s', hist.magic); % 4 chars from left + % fwrite(fid, magic(1:4), 'uchar'); + pad = zeros(1, 4 - length(hist.magic)); + hist.magic = [hist.magic char(pad)]; + fwrite(fid, hist.magic(1:4), 'uchar'); + + return % data_history diff --git a/lib/NiftiTools/save_untouch0_nii_hdr.m b/lib/NiftiTools/save_untouch0_nii_hdr.m index f52344a0..63d7ba78 100644 --- a/lib/NiftiTools/save_untouch0_nii_hdr.m +++ b/lib/NiftiTools/save_untouch0_nii_hdr.m @@ -4,216 +4,211 @@ function save_untouch0_nii_hdr(hdr, fid) - if ~isequal(hdr.hk.sizeof_hdr,348), - error('hdr.hk.sizeof_hdr must be 348.'); - end + if ~isequal(hdr.hk.sizeof_hdr, 348) + error('hdr.hk.sizeof_hdr must be 348.'); + end - write_header(hdr, fid); + write_header(hdr, fid); - return; % save_nii_hdr + return % save_nii_hdr - -%--------------------------------------------------------------------- + % --------------------------------------------------------------------- function write_header(hdr, fid) - % Original header structures - % struct dsr /* dsr = hdr */ - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ - - header_key(fid, hdr.hk); - image_dimension(fid, hdr.dime); - data_history(fid, hdr.hist); - - % check the file size is 348 bytes - % - fbytes = ftell(fid); - - if ~isequal(fbytes,348), - msg = sprintf('Header size is not 348 bytes.'); - warning(msg); - end - - return; % write_header - - -%--------------------------------------------------------------------- + % Original header structures + % struct dsr /* dsr = hdr */ + % { + % struct header_key hk; /* 0 + 40 */ + % struct image_dimension dime; /* 40 + 108 */ + % struct data_history hist; /* 148 + 200 */ + % }; /* total= 348 bytes*/ + + header_key(fid, hdr.hk); + image_dimension(fid, hdr.dime); + data_history(fid, hdr.hist); + + % check the file size is 348 bytes + % + fbytes = ftell(fid); + + if ~isequal(fbytes, 348) + msg = sprintf('Header size is not 348 bytes.'); + warning(msg); + end + + return % write_header + + % --------------------------------------------------------------------- function header_key(fid, hk) - - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - - fwrite(fid, hk.sizeof_hdr(1), 'int32'); % must be 348. - - % data_type = sprintf('%-10s',hk.data_type); % ensure it is 10 chars from left - % fwrite(fid, data_type(1:10), 'uchar'); - pad = zeros(1, 10-length(hk.data_type)); - hk.data_type = [hk.data_type char(pad)]; - fwrite(fid, hk.data_type(1:10), 'uchar'); - - % db_name = sprintf('%-18s', hk.db_name); % ensure it is 18 chars from left - % fwrite(fid, db_name(1:18), 'uchar'); - pad = zeros(1, 18-length(hk.db_name)); - hk.db_name = [hk.db_name char(pad)]; - fwrite(fid, hk.db_name(1:18), 'uchar'); - - fwrite(fid, hk.extents(1), 'int32'); - fwrite(fid, hk.session_error(1), 'int16'); - fwrite(fid, hk.regular(1), 'uchar'); - - fwrite(fid, hk.hkey_un0(1), 'uchar'); - - return; % header_key - - -%--------------------------------------------------------------------- + + fseek(fid, 0, 'bof'); + + % Original header structures + % struct header_key /* header key */ + % { /* off + size */ + % int sizeof_hdr /* 0 + 4 */ + % char data_type[10]; /* 4 + 10 */ + % char db_name[18]; /* 14 + 18 */ + % int extents; /* 32 + 4 */ + % short int session_error; /* 36 + 2 */ + % char regular; /* 38 + 1 */ + % char hkey_un0; /* 39 + 1 */ + % }; /* total=40 bytes */ + + fwrite(fid, hk.sizeof_hdr(1), 'int32'); % must be 348. + + % data_type = sprintf('%-10s',hk.data_type); % ensure it is 10 chars from left + % fwrite(fid, data_type(1:10), 'uchar'); + pad = zeros(1, 10 - length(hk.data_type)); + hk.data_type = [hk.data_type char(pad)]; + fwrite(fid, hk.data_type(1:10), 'uchar'); + + % db_name = sprintf('%-18s', hk.db_name); % ensure it is 18 chars from left + % fwrite(fid, db_name(1:18), 'uchar'); + pad = zeros(1, 18 - length(hk.db_name)); + hk.db_name = [hk.db_name char(pad)]; + fwrite(fid, hk.db_name(1:18), 'uchar'); + + fwrite(fid, hk.extents(1), 'int32'); + fwrite(fid, hk.session_error(1), 'int16'); + fwrite(fid, hk.regular(1), 'uchar'); + + fwrite(fid, hk.hkey_un0(1), 'uchar'); + + return % header_key + + % --------------------------------------------------------------------- function image_dimension(fid, dime) - %struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % char vox_units[4]; /* 16 + 4 */ - % char cal_units[8]; /* 20 + 8 */ - % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width - % pixdim[2] - voxel height - % pixdim[3] - interslice distance - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float roi_scale; /* 72 + 4 */ - % float funused1; /* 76 + 4 */ - % float funused2; /* 80 + 4 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % int compressed; /* 92 + 4 */ - % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - fwrite(fid, dime.dim(1:8), 'int16'); - - pad = zeros(1, 4-length(dime.vox_units)); - dime.vox_units = [dime.vox_units char(pad)]; - fwrite(fid, dime.vox_units(1:4), 'uchar'); - - pad = zeros(1, 8-length(dime.cal_units)); - dime.cal_units = [dime.cal_units char(pad)]; - fwrite(fid, dime.cal_units(1:8), 'uchar'); - - fwrite(fid, dime.unused1(1), 'int16'); - fwrite(fid, dime.datatype(1), 'int16'); - fwrite(fid, dime.bitpix(1), 'int16'); - fwrite(fid, dime.dim_un0(1), 'int16'); - fwrite(fid, dime.pixdim(1:8), 'float32'); - fwrite(fid, dime.vox_offset(1), 'float32'); - fwrite(fid, dime.roi_scale(1), 'float32'); - fwrite(fid, dime.funused1(1), 'float32'); - fwrite(fid, dime.funused2(1), 'float32'); - fwrite(fid, dime.cal_max(1), 'float32'); - fwrite(fid, dime.cal_min(1), 'float32'); - fwrite(fid, dime.compressed(1), 'int32'); - fwrite(fid, dime.verified(1), 'int32'); - fwrite(fid, dime.glmax(1), 'int32'); - fwrite(fid, dime.glmin(1), 'int32'); - - return; % image_dimension - - -%--------------------------------------------------------------------- + % struct image_dimension + % { /* off + size */ + % short int dim[8]; /* 0 + 16 */ + % char vox_units[4]; /* 16 + 4 */ + % char cal_units[8]; /* 20 + 8 */ + % short int unused1; /* 28 + 2 */ + % short int datatype; /* 30 + 2 */ + % short int bitpix; /* 32 + 2 */ + % short int dim_un0; /* 34 + 2 */ + % float pixdim[8]; /* 36 + 32 */ + % /* + % pixdim[] specifies the voxel dimensions: + % pixdim[1] - voxel width + % pixdim[2] - voxel height + % pixdim[3] - interslice distance + % ..etc + % */ + % float vox_offset; /* 68 + 4 */ + % float roi_scale; /* 72 + 4 */ + % float funused1; /* 76 + 4 */ + % float funused2; /* 80 + 4 */ + % float cal_max; /* 84 + 4 */ + % float cal_min; /* 88 + 4 */ + % int compressed; /* 92 + 4 */ + % int verified; /* 96 + 4 */ + % int glmax; /* 100 + 4 */ + % int glmin; /* 104 + 4 */ + % }; /* total=108 bytes */ + + fwrite(fid, dime.dim(1:8), 'int16'); + + pad = zeros(1, 4 - length(dime.vox_units)); + dime.vox_units = [dime.vox_units char(pad)]; + fwrite(fid, dime.vox_units(1:4), 'uchar'); + + pad = zeros(1, 8 - length(dime.cal_units)); + dime.cal_units = [dime.cal_units char(pad)]; + fwrite(fid, dime.cal_units(1:8), 'uchar'); + + fwrite(fid, dime.unused1(1), 'int16'); + fwrite(fid, dime.datatype(1), 'int16'); + fwrite(fid, dime.bitpix(1), 'int16'); + fwrite(fid, dime.dim_un0(1), 'int16'); + fwrite(fid, dime.pixdim(1:8), 'float32'); + fwrite(fid, dime.vox_offset(1), 'float32'); + fwrite(fid, dime.roi_scale(1), 'float32'); + fwrite(fid, dime.funused1(1), 'float32'); + fwrite(fid, dime.funused2(1), 'float32'); + fwrite(fid, dime.cal_max(1), 'float32'); + fwrite(fid, dime.cal_min(1), 'float32'); + fwrite(fid, dime.compressed(1), 'int32'); + fwrite(fid, dime.verified(1), 'int32'); + fwrite(fid, dime.glmax(1), 'int32'); + fwrite(fid, dime.glmin(1), 'int32'); + + return % image_dimension + + % --------------------------------------------------------------------- function data_history(fid, hist) - - % Original header structures - ANALYZE 7.5 - %struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % char orient; /* 104 + 1 */ - % char originator[10]; /* 105 + 10 */ - % char generated[10]; /* 115 + 10 */ - % char scannum[10]; /* 125 + 10 */ - % char patient_id[10]; /* 135 + 10 */ - % char exp_date[10]; /* 145 + 10 */ - % char exp_time[10]; /* 155 + 10 */ - % char hist_un0[3]; /* 165 + 3 */ - % int views /* 168 + 4 */ - % int vols_added; /* 172 + 4 */ - % int start_field; /* 176 + 4 */ - % int field_skip; /* 180 + 4 */ - % int omax; /* 184 + 4 */ - % int omin; /* 188 + 4 */ - % int smax; /* 192 + 4 */ - % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - % descrip = sprintf('%-80s', hist.descrip); % 80 chars from left - % fwrite(fid, descrip(1:80), 'uchar'); - pad = zeros(1, 80-length(hist.descrip)); - hist.descrip = [hist.descrip char(pad)]; - fwrite(fid, hist.descrip(1:80), 'uchar'); - - % aux_file = sprintf('%-24s', hist.aux_file); % 24 chars from left - % fwrite(fid, aux_file(1:24), 'uchar'); - pad = zeros(1, 24-length(hist.aux_file)); - hist.aux_file = [hist.aux_file char(pad)]; - fwrite(fid, hist.aux_file(1:24), 'uchar'); - - fwrite(fid, hist.orient(1), 'uchar'); - fwrite(fid, hist.originator(1:5), 'int16'); - - pad = zeros(1, 10-length(hist.generated)); - hist.generated = [hist.generated char(pad)]; - fwrite(fid, hist.generated(1:10), 'uchar'); - - pad = zeros(1, 10-length(hist.scannum)); - hist.scannum = [hist.scannum char(pad)]; - fwrite(fid, hist.scannum(1:10), 'uchar'); - - pad = zeros(1, 10-length(hist.patient_id)); - hist.patient_id = [hist.patient_id char(pad)]; - fwrite(fid, hist.patient_id(1:10), 'uchar'); - - pad = zeros(1, 10-length(hist.exp_date)); - hist.exp_date = [hist.exp_date char(pad)]; - fwrite(fid, hist.exp_date(1:10), 'uchar'); - - pad = zeros(1, 10-length(hist.exp_time)); - hist.exp_time = [hist.exp_time char(pad)]; - fwrite(fid, hist.exp_time(1:10), 'uchar'); - - pad = zeros(1, 3-length(hist.hist_un0)); - hist.hist_un0 = [hist.hist_un0 char(pad)]; - fwrite(fid, hist.hist_un0(1:3), 'uchar'); - - fwrite(fid, hist.views(1), 'int32'); - fwrite(fid, hist.vols_added(1), 'int32'); - fwrite(fid, hist.start_field(1),'int32'); - fwrite(fid, hist.field_skip(1), 'int32'); - fwrite(fid, hist.omax(1), 'int32'); - fwrite(fid, hist.omin(1), 'int32'); - fwrite(fid, hist.smax(1), 'int32'); - fwrite(fid, hist.smin(1), 'int32'); - - return; % data_history + % Original header structures - ANALYZE 7.5 + % struct data_history + % { /* off + size */ + % char descrip[80]; /* 0 + 80 */ + % char aux_file[24]; /* 80 + 24 */ + % char orient; /* 104 + 1 */ + % char originator[10]; /* 105 + 10 */ + % char generated[10]; /* 115 + 10 */ + % char scannum[10]; /* 125 + 10 */ + % char patient_id[10]; /* 135 + 10 */ + % char exp_date[10]; /* 145 + 10 */ + % char exp_time[10]; /* 155 + 10 */ + % char hist_un0[3]; /* 165 + 3 */ + % int views /* 168 + 4 */ + % int vols_added; /* 172 + 4 */ + % int start_field; /* 176 + 4 */ + % int field_skip; /* 180 + 4 */ + % int omax; /* 184 + 4 */ + % int omin; /* 188 + 4 */ + % int smax; /* 192 + 4 */ + % int smin; /* 196 + 4 */ + % }; /* total=200 bytes */ + + % descrip = sprintf('%-80s', hist.descrip); % 80 chars from left + % fwrite(fid, descrip(1:80), 'uchar'); + pad = zeros(1, 80 - length(hist.descrip)); + hist.descrip = [hist.descrip char(pad)]; + fwrite(fid, hist.descrip(1:80), 'uchar'); + + % aux_file = sprintf('%-24s', hist.aux_file); % 24 chars from left + % fwrite(fid, aux_file(1:24), 'uchar'); + pad = zeros(1, 24 - length(hist.aux_file)); + hist.aux_file = [hist.aux_file char(pad)]; + fwrite(fid, hist.aux_file(1:24), 'uchar'); + + fwrite(fid, hist.orient(1), 'uchar'); + fwrite(fid, hist.originator(1:5), 'int16'); + + pad = zeros(1, 10 - length(hist.generated)); + hist.generated = [hist.generated char(pad)]; + fwrite(fid, hist.generated(1:10), 'uchar'); + + pad = zeros(1, 10 - length(hist.scannum)); + hist.scannum = [hist.scannum char(pad)]; + fwrite(fid, hist.scannum(1:10), 'uchar'); + + pad = zeros(1, 10 - length(hist.patient_id)); + hist.patient_id = [hist.patient_id char(pad)]; + fwrite(fid, hist.patient_id(1:10), 'uchar'); + + pad = zeros(1, 10 - length(hist.exp_date)); + hist.exp_date = [hist.exp_date char(pad)]; + fwrite(fid, hist.exp_date(1:10), 'uchar'); + + pad = zeros(1, 10 - length(hist.exp_time)); + hist.exp_time = [hist.exp_time char(pad)]; + fwrite(fid, hist.exp_time(1:10), 'uchar'); + + pad = zeros(1, 3 - length(hist.hist_un0)); + hist.hist_un0 = [hist.hist_un0 char(pad)]; + fwrite(fid, hist.hist_un0(1:3), 'uchar'); + + fwrite(fid, hist.views(1), 'int32'); + fwrite(fid, hist.vols_added(1), 'int32'); + fwrite(fid, hist.start_field(1), 'int32'); + fwrite(fid, hist.field_skip(1), 'int32'); + fwrite(fid, hist.omax(1), 'int32'); + fwrite(fid, hist.omin(1), 'int32'); + fwrite(fid, hist.smax(1), 'int32'); + fwrite(fid, hist.smin(1), 'int32'); + + return % data_history diff --git a/lib/NiftiTools/save_untouch_header_only.m b/lib/NiftiTools/save_untouch_header_only.m index b1450582..e366e64e 100644 --- a/lib/NiftiTools/save_untouch_header_only.m +++ b/lib/NiftiTools/save_untouch_header_only.m @@ -1,71 +1,69 @@ % This function is only used to save Analyze or NIfTI header that is -% ended with .hdr and loaded by load_untouch_header_only.m. If you +% ended with .hdr and loaded by load_untouch_header_only.m. If you % have NIfTI file that is ended with .nii and you want to change its % header only, you can use load_untouch_nii / save_untouch_nii pair. -% +% % Usage: save_untouch_header_only(hdr, new_header_file_name) -% +% % hdr - struct with NIfTI / Analyze header fields, which is obtained from: % hdr = load_untouch_header_only(original_header_file_name) -% +% % new_header_file_name - NIfTI / Analyze header name ended with .hdr. % You can either copy original.img(.gz) to new.img(.gz) manually, % or simply input original.hdr(.gz) in save_untouch_header_only.m % to overwrite the original header. -% +% % - Jimmy Shen (jshen@research.baycrest.org) % function save_untouch_header_only(hdr, filename) - if ~exist('hdr','var') | isempty(hdr) | ~exist('filename','var') | isempty(filename) - error('Usage: save_untouch_header_only(hdr, filename)'); - end - - v = version; + if ~exist('hdr', 'var') | isempty(hdr) | ~exist('filename', 'var') | isempty(filename) + error('Usage: save_untouch_header_only(hdr, filename)'); + end - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') + v = version; - if ~strcmp(filename(end-6:end), '.hdr.gz') - error('Please check filename.'); - end + % Check file extension. If .gz, unpack it into temp folder + % + if length(filename) > 2 & strcmp(filename(end - 2:end), '.gz') - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - else - gzFile = 1; - filename = filename(1:end-3); - end - end + if ~strcmp(filename(end - 6:end), '.hdr.gz') + error('Please check filename.'); + end - [p,f] = fileparts(filename); - fileprefix = fullfile(p, f); + if str2num(v(1:3)) < 7.1 | ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + else + gzFile = 1; + filename = filename(1:end - 3); + end + end - write_hdr(hdr, fileprefix); + [p, f] = fileparts(filename); + fileprefix = fullfile(p, f); - % gzip output file if requested - % - if exist('gzFile', 'var') - gzip([fileprefix, '.hdr']); - delete([fileprefix, '.hdr']); - end; + write_hdr(hdr, fileprefix); - return % save_untouch_header_only + % gzip output file if requested + % + if exist('gzFile', 'var') + gzip([fileprefix, '.hdr']); + delete([fileprefix, '.hdr']); + end + return % save_untouch_header_only -%----------------------------------------------------------------------------------- + % ----------------------------------------------------------------------------------- function write_hdr(hdr, fileprefix) - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if isfield(hdr.hist,'magic') - save_untouch_nii_hdr(hdr, fid); - else - save_untouch0_nii_hdr(hdr, fid); - end + fid = fopen(sprintf('%s.hdr', fileprefix), 'w'); - fclose(fid); + if isfield(hdr.hist, 'magic') + save_untouch_nii_hdr(hdr, fid); + else + save_untouch0_nii_hdr(hdr, fid); + end - return % write_hdr + fclose(fid); + return % write_hdr diff --git a/lib/NiftiTools/save_untouch_nii.m b/lib/NiftiTools/save_untouch_nii.m index c7001113..18a90151 100644 --- a/lib/NiftiTools/save_untouch_nii.m +++ b/lib/NiftiTools/save_untouch_nii.m @@ -4,229 +4,241 @@ % extension that you specified will be ignored. % % Usage: save_untouch_nii(nii, filename) -% +% % nii - nii structure that is loaded by "load_untouch_nii.m" % -% filename - NIFTI or ANALYZE file name. +% filename - NIFTI or ANALYZE file name. % % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % function save_untouch_nii(nii, filename) - - if ~exist('nii','var') || isempty(nii) || ~isfield(nii,'hdr') || ... - ~isfield(nii,'img') || ~exist('filename','var') || isempty(filename) - - error('Usage: save_untouch_nii(nii, filename)'); - end - - if ~isfield(nii,'untouch') || nii.untouch == 0 - error('Usage: please use ''save_nii.m'' for the modified structure.'); - end - - if isfield(nii.hdr.hist,'magic') & strcmp(nii.hdr.hist.magic(1:3),'ni1') - filetype = 1; - elseif isfield(nii.hdr.hist,'magic') & strcmp(nii.hdr.hist.magic(1:3),'n+1') - filetype = 2; - else - filetype = 0; - end - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 || ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - else - gzFile = 1; - filename = filename(1:end-3); - end - end - - [p,f] = fileparts(filename); - fileprefix = fullfile(p, f); - - write_nii(nii, filetype, fileprefix); - - % gzip output file if requested - % - if exist('gzFile', 'var') - if filetype == 1 - gzip([fileprefix, '.img']); - delete([fileprefix, '.img']); - gzip([fileprefix, '.hdr']); - delete([fileprefix, '.hdr']); - elseif filetype == 2 - gzip([fileprefix, '.nii']); - delete([fileprefix, '.nii']); - end; - end; - -% % So earlier versions of SPM can also open it with correct originator - % % + + if ~exist('nii', 'var') || isempty(nii) || ~isfield(nii, 'hdr') || ... + ~isfield(nii, 'img') || ~exist('filename', 'var') || isempty(filename) + + error('Usage: save_untouch_nii(nii, filename)'); + end + + if ~isfield(nii, 'untouch') || nii.untouch == 0 + error('Usage: please use ''save_nii.m'' for the modified structure.'); + end + + if isfield(nii.hdr.hist, 'magic') & strcmp(nii.hdr.hist.magic(1:3), 'ni1') + filetype = 1; + elseif isfield(nii.hdr.hist, 'magic') & strcmp(nii.hdr.hist.magic(1:3), 'n+1') + filetype = 2; + else + filetype = 0; + end + + v = version; + + % Check file extension. If .gz, unpack it into temp folder + % + if length(filename) > 2 & strcmp(filename(end - 2:end), '.gz') + + if ~strcmp(filename(end - 6:end), '.img.gz') & ... + ~strcmp(filename(end - 6:end), '.hdr.gz') & ... + ~strcmp(filename(end - 6:end), '.nii.gz') + + error('Please check filename.'); + end + + if str2num(v(1:3)) < 7.1 || ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + else + gzFile = 1; + filename = filename(1:end - 3); + end + end + + [p, f] = fileparts(filename); + fileprefix = fullfile(p, f); + + write_nii(nii, filetype, fileprefix); + + % gzip output file if requested + % + if exist('gzFile', 'var') + if filetype == 1 + gzip([fileprefix, '.img']); + delete([fileprefix, '.img']); + gzip([fileprefix, '.hdr']); + delete([fileprefix, '.hdr']); + elseif filetype == 2 + gzip([fileprefix, '.nii']); + delete([fileprefix, '.nii']); + end + end + + % % So earlier versions of SPM can also open it with correct originator + % % % if filetype == 0 - % M=[[diag(nii.hdr.dime.pixdim(2:4)) -[nii.hdr.hist.originator(1:3).*nii.hdr.dime.pixdim(2:4)]'];[0 0 0 1]]; - % save(fileprefix, 'M'); -% elseif filetype == 1 - % M=[]; + % M=[[diag(nii.hdr.dime.pixdim(2:4)) -[nii.hdr.hist.originator(1:3).*nii.hdr.dime.pixdim(2:4)]'];[0 0 0 1]]; + % save(fileprefix, 'M'); + % elseif filetype == 1 + % M=[]; % save(fileprefix, 'M'); - %end - - return % save_untouch_nii + % end + return % save_untouch_nii -%----------------------------------------------------------------------------------- + % ----------------------------------------------------------------------------------- function write_nii(nii, filetype, fileprefix) - hdr = nii.hdr; - - if isfield(nii,'ext') & ~isempty(nii.ext) - ext = nii.ext; - [ext, esize_total] = verify_nii_ext(ext); - else - ext = []; - end - - switch double(hdr.dime.datatype), - case 1, - hdr.dime.bitpix = int16(1 ); precision = 'ubit1'; - case 2, - hdr.dime.bitpix = int16(8 ); precision = 'uint8'; - case 4, - hdr.dime.bitpix = int16(16); precision = 'int16'; - case 8, - hdr.dime.bitpix = int16(32); precision = 'int32'; - case 16, - hdr.dime.bitpix = int16(32); precision = 'float32'; - case 32, - hdr.dime.bitpix = int16(64); precision = 'float32'; - case 64, - hdr.dime.bitpix = int16(64); precision = 'float64'; - case 128, - hdr.dime.bitpix = int16(24); precision = 'uint8'; - case 256 - hdr.dime.bitpix = int16(8 ); precision = 'int8'; - case 512 - hdr.dime.bitpix = int16(16); precision = 'uint16'; - case 768 - hdr.dime.bitpix = int16(32); precision = 'uint32'; - case 1024 - hdr.dime.bitpix = int16(64); precision = 'int64'; - case 1280 - hdr.dime.bitpix = int16(64); precision = 'uint64'; - case 1792, - hdr.dime.bitpix = int16(128); precision = 'float64'; - otherwise + hdr = nii.hdr; + + if isfield(nii, 'ext') & ~isempty(nii.ext) + ext = nii.ext; + [ext, esize_total] = verify_nii_ext(ext); + else + ext = []; + end + + switch double(hdr.dime.datatype) + case 1 + hdr.dime.bitpix = int16(1); + precision = 'ubit1'; + case 2 + hdr.dime.bitpix = int16(8); + precision = 'uint8'; + case 4 + hdr.dime.bitpix = int16(16); + precision = 'int16'; + case 8 + hdr.dime.bitpix = int16(32); + precision = 'int32'; + case 16 + hdr.dime.bitpix = int16(32); + precision = 'float32'; + case 32 + hdr.dime.bitpix = int16(64); + precision = 'float32'; + case 64 + hdr.dime.bitpix = int16(64); + precision = 'float64'; + case 128 + hdr.dime.bitpix = int16(24); + precision = 'uint8'; + case 256 + hdr.dime.bitpix = int16(8); + precision = 'int8'; + case 512 + hdr.dime.bitpix = int16(16); + precision = 'uint16'; + case 768 + hdr.dime.bitpix = int16(32); + precision = 'uint32'; + case 1024 + hdr.dime.bitpix = int16(64); + precision = 'int64'; + case 1280 + hdr.dime.bitpix = int16(64); + precision = 'uint64'; + case 1792 + hdr.dime.bitpix = int16(128); + precision = 'float64'; + otherwise error('This datatype is not supported'); - end - -% hdr.dime.glmax = round(double(max(nii.img(:)))); - % hdr.dime.glmin = round(double(min(nii.img(:)))); - - if filetype == 2 - fid = fopen(sprintf('%s.nii',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.nii.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 352; - - if ~isempty(ext) - hdr.dime.vox_offset = hdr.dime.vox_offset + esize_total; - end - - hdr.hist.magic = 'n+1'; - save_untouch_nii_hdr(hdr, fid); - - if ~isempty(ext) - save_nii_ext(ext, fid); - end - elseif filetype == 1 - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 0; - hdr.hist.magic = 'ni1'; - save_untouch_nii_hdr(hdr, fid); - - if ~isempty(ext) - save_nii_ext(ext, fid); - end - - fclose(fid); - fid = fopen(sprintf('%s.img',fileprefix),'w'); - else - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - save_untouch0_nii_hdr(hdr, fid); - - fclose(fid); - fid = fopen(sprintf('%s.img',fileprefix),'w'); - end - - ScanDim = double(hdr.dime.dim(5)); % t - SliceDim = double(hdr.dime.dim(4)); % z - RowDim = double(hdr.dime.dim(3)); % y - PixelDim = double(hdr.dime.dim(2)); % x - SliceSz = double(hdr.dime.pixdim(4)); - RowSz = double(hdr.dime.pixdim(3)); - PixelSz = double(hdr.dime.pixdim(2)); - - x = 1:PixelDim; - - if filetype == 2 & isempty(ext) - skip_bytes = double(hdr.dime.vox_offset) - 348; - else - skip_bytes = 0; - end - - if double(hdr.dime.datatype) == 128 - - % RGB planes are expected to be in the 4th dimension of nii.img - % - if(size(nii.img,4)~=3) - error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); - end - - nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); - end - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 - real_img = real(nii.img(:))'; - nii.img = imag(nii.img(:))'; - nii.img = [real_img; nii.img]; - end - - if skip_bytes - fwrite(fid, zeros(1,skip_bytes), 'uint8'); - end - - fwrite(fid, nii.img, precision); -% fwrite(fid, nii.img, precision, skip_bytes); % error using skip - fclose(fid); - - return; % write_nii - + end + + % hdr.dime.glmax = round(double(max(nii.img(:)))); + % hdr.dime.glmin = round(double(min(nii.img(:)))); + + if filetype == 2 + fid = fopen(sprintf('%s.nii', fileprefix), 'w'); + + if fid < 0 + msg = sprintf('Cannot open file %s.nii.', fileprefix); + error(msg); + end + + hdr.dime.vox_offset = 352; + + if ~isempty(ext) + hdr.dime.vox_offset = hdr.dime.vox_offset + esize_total; + end + + hdr.hist.magic = 'n+1'; + save_untouch_nii_hdr(hdr, fid); + + if ~isempty(ext) + save_nii_ext(ext, fid); + end + elseif filetype == 1 + fid = fopen(sprintf('%s.hdr', fileprefix), 'w'); + + if fid < 0 + msg = sprintf('Cannot open file %s.hdr.', fileprefix); + error(msg); + end + + hdr.dime.vox_offset = 0; + hdr.hist.magic = 'ni1'; + save_untouch_nii_hdr(hdr, fid); + + if ~isempty(ext) + save_nii_ext(ext, fid); + end + + fclose(fid); + fid = fopen(sprintf('%s.img', fileprefix), 'w'); + else + fid = fopen(sprintf('%s.hdr', fileprefix), 'w'); + + if fid < 0 + msg = sprintf('Cannot open file %s.hdr.', fileprefix); + error(msg); + end + + save_untouch0_nii_hdr(hdr, fid); + + fclose(fid); + fid = fopen(sprintf('%s.img', fileprefix), 'w'); + end + + ScanDim = double(hdr.dime.dim(5)); % t + SliceDim = double(hdr.dime.dim(4)); % z + RowDim = double(hdr.dime.dim(3)); % y + PixelDim = double(hdr.dime.dim(2)); % x + SliceSz = double(hdr.dime.pixdim(4)); + RowSz = double(hdr.dime.pixdim(3)); + PixelSz = double(hdr.dime.pixdim(2)); + + x = 1:PixelDim; + + if filetype == 2 & isempty(ext) + skip_bytes = double(hdr.dime.vox_offset) - 348; + else + skip_bytes = 0; + end + + if double(hdr.dime.datatype) == 128 + + % RGB planes are expected to be in the 4th dimension of nii.img + % + if size(nii.img, 4) ~= 3 + error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); + end + + nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); + end + + % For complex float32 or complex float64, voxel values + % include [real, imag] + % + if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 + real_img = real(nii.img(:))'; + nii.img = imag(nii.img(:))'; + nii.img = [real_img; nii.img]; + end + + if skip_bytes + fwrite(fid, zeros(1, skip_bytes), 'uint8'); + end + + fwrite(fid, nii.img, precision); + % fwrite(fid, nii.img, precision, skip_bytes); % error using skip + fclose(fid); + + return % write_nii diff --git a/lib/NiftiTools/save_untouch_nii_hdr.m b/lib/NiftiTools/save_untouch_nii_hdr.m index 1400dea4..4bd483fb 100644 --- a/lib/NiftiTools/save_untouch_nii_hdr.m +++ b/lib/NiftiTools/save_untouch_nii_hdr.m @@ -4,204 +4,199 @@ function save_untouch_nii_hdr(hdr, fid) - if ~isequal(hdr.hk.sizeof_hdr,348), - error('hdr.hk.sizeof_hdr must be 348.'); - end + if ~isequal(hdr.hk.sizeof_hdr, 348) + error('hdr.hk.sizeof_hdr must be 348.'); + end - write_header(hdr, fid); + write_header(hdr, fid); - return; % save_nii_hdr + return % save_nii_hdr - -%--------------------------------------------------------------------- + % --------------------------------------------------------------------- function write_header(hdr, fid) - % Original header structures - % struct dsr /* dsr = hdr */ - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ - - header_key(fid, hdr.hk); - image_dimension(fid, hdr.dime); - data_history(fid, hdr.hist); - - % check the file size is 348 bytes - % - fbytes = ftell(fid); - - if ~isequal(fbytes,348), - msg = sprintf('Header size is not 348 bytes.'); - warning(msg); - end - - return; % write_header - - -%--------------------------------------------------------------------- + % Original header structures + % struct dsr /* dsr = hdr */ + % { + % struct header_key hk; /* 0 + 40 */ + % struct image_dimension dime; /* 40 + 108 */ + % struct data_history hist; /* 148 + 200 */ + % }; /* total= 348 bytes*/ + + header_key(fid, hdr.hk); + image_dimension(fid, hdr.dime); + data_history(fid, hdr.hist); + + % check the file size is 348 bytes + % + fbytes = ftell(fid); + + if ~isequal(fbytes, 348) + msg = sprintf('Header size is not 348 bytes.'); + warning(msg); + end + + return % write_header + + % --------------------------------------------------------------------- function header_key(fid, hk) - - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char dim_info; % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - - fwrite(fid, hk.sizeof_hdr(1), 'int32'); % must be 348. - - % data_type = sprintf('%-10s',hk.data_type); % ensure it is 10 chars from left - % fwrite(fid, data_type(1:10), 'uchar'); - pad = zeros(1, 10-length(hk.data_type)); - hk.data_type = [hk.data_type char(pad)]; - fwrite(fid, hk.data_type(1:10), 'uchar'); - - % db_name = sprintf('%-18s', hk.db_name); % ensure it is 18 chars from left - % fwrite(fid, db_name(1:18), 'uchar'); - pad = zeros(1, 18-length(hk.db_name)); - hk.db_name = [hk.db_name char(pad)]; - fwrite(fid, hk.db_name(1:18), 'uchar'); - - fwrite(fid, hk.extents(1), 'int32'); - fwrite(fid, hk.session_error(1), 'int16'); - fwrite(fid, hk.regular(1), 'uchar'); % might be uint8 - - % fwrite(fid, hk.hkey_un0(1), 'uchar'); - % fwrite(fid, hk.hkey_un0(1), 'uint8'); - fwrite(fid, hk.dim_info(1), 'uchar'); - - return; % header_key - - -%--------------------------------------------------------------------- + + fseek(fid, 0, 'bof'); + + % Original header structures + % struct header_key /* header key */ + % { /* off + size */ + % int sizeof_hdr /* 0 + 4 */ + % char data_type[10]; /* 4 + 10 */ + % char db_name[18]; /* 14 + 18 */ + % int extents; /* 32 + 4 */ + % short int session_error; /* 36 + 2 */ + % char regular; /* 38 + 1 */ + % char dim_info; % char hkey_un0; /* 39 + 1 */ + % }; /* total=40 bytes */ + + fwrite(fid, hk.sizeof_hdr(1), 'int32'); % must be 348. + + % data_type = sprintf('%-10s',hk.data_type); % ensure it is 10 chars from left + % fwrite(fid, data_type(1:10), 'uchar'); + pad = zeros(1, 10 - length(hk.data_type)); + hk.data_type = [hk.data_type char(pad)]; + fwrite(fid, hk.data_type(1:10), 'uchar'); + + % db_name = sprintf('%-18s', hk.db_name); % ensure it is 18 chars from left + % fwrite(fid, db_name(1:18), 'uchar'); + pad = zeros(1, 18 - length(hk.db_name)); + hk.db_name = [hk.db_name char(pad)]; + fwrite(fid, hk.db_name(1:18), 'uchar'); + + fwrite(fid, hk.extents(1), 'int32'); + fwrite(fid, hk.session_error(1), 'int16'); + fwrite(fid, hk.regular(1), 'uchar'); % might be uint8 + + % fwrite(fid, hk.hkey_un0(1), 'uchar'); + % fwrite(fid, hk.hkey_un0(1), 'uint8'); + fwrite(fid, hk.dim_info(1), 'uchar'); + + return % header_key + + % --------------------------------------------------------------------- function image_dimension(fid, dime) - % Original header structures - % struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % float intent_p1; % char vox_units[4]; /* 16 + 4 */ - % float intent_p2; % char cal_units[8]; /* 20 + 4 */ - % float intent_p3; % char cal_units[8]; /* 24 + 4 */ - % short int intent_code; % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int slice_start; % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width - % pixdim[2] - voxel height - % pixdim[3] - interslice distance - % pixdim[4] - volume timing, in msec - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float scl_slope; % float roi_scale; /* 72 + 4 */ - % float scl_inter; % float funused1; /* 76 + 4 */ - % short slice_end; % float funused2; /* 80 + 2 */ - % char slice_code; % float funused2; /* 82 + 1 */ - % char xyzt_units; % float funused2; /* 83 + 1 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % float slice_duration; % int compressed; /* 92 + 4 */ - % float toffset; % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - fwrite(fid, dime.dim(1:8), 'int16'); - fwrite(fid, dime.intent_p1(1), 'float32'); - fwrite(fid, dime.intent_p2(1), 'float32'); - fwrite(fid, dime.intent_p3(1), 'float32'); - fwrite(fid, dime.intent_code(1), 'int16'); - fwrite(fid, dime.datatype(1), 'int16'); - fwrite(fid, dime.bitpix(1), 'int16'); - fwrite(fid, dime.slice_start(1), 'int16'); - fwrite(fid, dime.pixdim(1:8), 'float32'); - fwrite(fid, dime.vox_offset(1), 'float32'); - fwrite(fid, dime.scl_slope(1), 'float32'); - fwrite(fid, dime.scl_inter(1), 'float32'); - fwrite(fid, dime.slice_end(1), 'int16'); - fwrite(fid, dime.slice_code(1), 'uchar'); - fwrite(fid, dime.xyzt_units(1), 'uchar'); - fwrite(fid, dime.cal_max(1), 'float32'); - fwrite(fid, dime.cal_min(1), 'float32'); - fwrite(fid, dime.slice_duration(1), 'float32'); - fwrite(fid, dime.toffset(1), 'float32'); - fwrite(fid, dime.glmax(1), 'int32'); - fwrite(fid, dime.glmin(1), 'int32'); - - return; % image_dimension - - -%--------------------------------------------------------------------- + % Original header structures + % struct image_dimension + % { /* off + size */ + % short int dim[8]; /* 0 + 16 */ + % float intent_p1; % char vox_units[4]; /* 16 + 4 */ + % float intent_p2; % char cal_units[8]; /* 20 + 4 */ + % float intent_p3; % char cal_units[8]; /* 24 + 4 */ + % short int intent_code; % short int unused1; /* 28 + 2 */ + % short int datatype; /* 30 + 2 */ + % short int bitpix; /* 32 + 2 */ + % short int slice_start; % short int dim_un0; /* 34 + 2 */ + % float pixdim[8]; /* 36 + 32 */ + % /* + % pixdim[] specifies the voxel dimensions: + % pixdim[1] - voxel width + % pixdim[2] - voxel height + % pixdim[3] - interslice distance + % pixdim[4] - volume timing, in msec + % ..etc + % */ + % float vox_offset; /* 68 + 4 */ + % float scl_slope; % float roi_scale; /* 72 + 4 */ + % float scl_inter; % float funused1; /* 76 + 4 */ + % short slice_end; % float funused2; /* 80 + 2 */ + % char slice_code; % float funused2; /* 82 + 1 */ + % char xyzt_units; % float funused2; /* 83 + 1 */ + % float cal_max; /* 84 + 4 */ + % float cal_min; /* 88 + 4 */ + % float slice_duration; % int compressed; /* 92 + 4 */ + % float toffset; % int verified; /* 96 + 4 */ + % int glmax; /* 100 + 4 */ + % int glmin; /* 104 + 4 */ + % }; /* total=108 bytes */ + + fwrite(fid, dime.dim(1:8), 'int16'); + fwrite(fid, dime.intent_p1(1), 'float32'); + fwrite(fid, dime.intent_p2(1), 'float32'); + fwrite(fid, dime.intent_p3(1), 'float32'); + fwrite(fid, dime.intent_code(1), 'int16'); + fwrite(fid, dime.datatype(1), 'int16'); + fwrite(fid, dime.bitpix(1), 'int16'); + fwrite(fid, dime.slice_start(1), 'int16'); + fwrite(fid, dime.pixdim(1:8), 'float32'); + fwrite(fid, dime.vox_offset(1), 'float32'); + fwrite(fid, dime.scl_slope(1), 'float32'); + fwrite(fid, dime.scl_inter(1), 'float32'); + fwrite(fid, dime.slice_end(1), 'int16'); + fwrite(fid, dime.slice_code(1), 'uchar'); + fwrite(fid, dime.xyzt_units(1), 'uchar'); + fwrite(fid, dime.cal_max(1), 'float32'); + fwrite(fid, dime.cal_min(1), 'float32'); + fwrite(fid, dime.slice_duration(1), 'float32'); + fwrite(fid, dime.toffset(1), 'float32'); + fwrite(fid, dime.glmax(1), 'int32'); + fwrite(fid, dime.glmin(1), 'int32'); + + return % image_dimension + + % --------------------------------------------------------------------- function data_history(fid, hist) - - % Original header structures - %struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % short int qform_code; /* 104 + 2 */ - % short int sform_code; /* 106 + 2 */ - % float quatern_b; /* 108 + 4 */ - % float quatern_c; /* 112 + 4 */ - % float quatern_d; /* 116 + 4 */ - % float qoffset_x; /* 120 + 4 */ - % float qoffset_y; /* 124 + 4 */ - % float qoffset_z; /* 128 + 4 */ - % float srow_x[4]; /* 132 + 16 */ - % float srow_y[4]; /* 148 + 16 */ - % float srow_z[4]; /* 164 + 16 */ - % char intent_name[16]; /* 180 + 16 */ - % char magic[4]; % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - % descrip = sprintf('%-80s', hist.descrip); % 80 chars from left - % fwrite(fid, descrip(1:80), 'uchar'); - pad = zeros(1, 80-length(hist.descrip)); - hist.descrip = [hist.descrip char(pad)]; - fwrite(fid, hist.descrip(1:80), 'uchar'); - - % aux_file = sprintf('%-24s', hist.aux_file); % 24 chars from left - % fwrite(fid, aux_file(1:24), 'uchar'); - pad = zeros(1, 24-length(hist.aux_file)); - hist.aux_file = [hist.aux_file char(pad)]; - fwrite(fid, hist.aux_file(1:24), 'uchar'); - - fwrite(fid, hist.qform_code, 'int16'); - fwrite(fid, hist.sform_code, 'int16'); - fwrite(fid, hist.quatern_b, 'float32'); - fwrite(fid, hist.quatern_c, 'float32'); - fwrite(fid, hist.quatern_d, 'float32'); - fwrite(fid, hist.qoffset_x, 'float32'); - fwrite(fid, hist.qoffset_y, 'float32'); - fwrite(fid, hist.qoffset_z, 'float32'); - fwrite(fid, hist.srow_x(1:4), 'float32'); - fwrite(fid, hist.srow_y(1:4), 'float32'); - fwrite(fid, hist.srow_z(1:4), 'float32'); - - % intent_name = sprintf('%-16s', hist.intent_name); % 16 chars from left - % fwrite(fid, intent_name(1:16), 'uchar'); - pad = zeros(1, 16-length(hist.intent_name)); - hist.intent_name = [hist.intent_name char(pad)]; - fwrite(fid, hist.intent_name(1:16), 'uchar'); - - % magic = sprintf('%-4s', hist.magic); % 4 chars from left - % fwrite(fid, magic(1:4), 'uchar'); - pad = zeros(1, 4-length(hist.magic)); - hist.magic = [hist.magic char(pad)]; - fwrite(fid, hist.magic(1:4), 'uchar'); - - return; % data_history + % Original header structures + % struct data_history + % { /* off + size */ + % char descrip[80]; /* 0 + 80 */ + % char aux_file[24]; /* 80 + 24 */ + % short int qform_code; /* 104 + 2 */ + % short int sform_code; /* 106 + 2 */ + % float quatern_b; /* 108 + 4 */ + % float quatern_c; /* 112 + 4 */ + % float quatern_d; /* 116 + 4 */ + % float qoffset_x; /* 120 + 4 */ + % float qoffset_y; /* 124 + 4 */ + % float qoffset_z; /* 128 + 4 */ + % float srow_x[4]; /* 132 + 16 */ + % float srow_y[4]; /* 148 + 16 */ + % float srow_z[4]; /* 164 + 16 */ + % char intent_name[16]; /* 180 + 16 */ + % char magic[4]; % int smin; /* 196 + 4 */ + % }; /* total=200 bytes */ + + % descrip = sprintf('%-80s', hist.descrip); % 80 chars from left + % fwrite(fid, descrip(1:80), 'uchar'); + pad = zeros(1, 80 - length(hist.descrip)); + hist.descrip = [hist.descrip char(pad)]; + fwrite(fid, hist.descrip(1:80), 'uchar'); + + % aux_file = sprintf('%-24s', hist.aux_file); % 24 chars from left + % fwrite(fid, aux_file(1:24), 'uchar'); + pad = zeros(1, 24 - length(hist.aux_file)); + hist.aux_file = [hist.aux_file char(pad)]; + fwrite(fid, hist.aux_file(1:24), 'uchar'); + + fwrite(fid, hist.qform_code, 'int16'); + fwrite(fid, hist.sform_code, 'int16'); + fwrite(fid, hist.quatern_b, 'float32'); + fwrite(fid, hist.quatern_c, 'float32'); + fwrite(fid, hist.quatern_d, 'float32'); + fwrite(fid, hist.qoffset_x, 'float32'); + fwrite(fid, hist.qoffset_y, 'float32'); + fwrite(fid, hist.qoffset_z, 'float32'); + fwrite(fid, hist.srow_x(1:4), 'float32'); + fwrite(fid, hist.srow_y(1:4), 'float32'); + fwrite(fid, hist.srow_z(1:4), 'float32'); + + % intent_name = sprintf('%-16s', hist.intent_name); % 16 chars from left + % fwrite(fid, intent_name(1:16), 'uchar'); + pad = zeros(1, 16 - length(hist.intent_name)); + hist.intent_name = [hist.intent_name char(pad)]; + fwrite(fid, hist.intent_name(1:16), 'uchar'); + + % magic = sprintf('%-4s', hist.magic); % 4 chars from left + % fwrite(fid, magic(1:4), 'uchar'); + pad = zeros(1, 4 - length(hist.magic)); + hist.magic = [hist.magic char(pad)]; + fwrite(fid, hist.magic(1:4), 'uchar'); + + return % data_history diff --git a/lib/NiftiTools/save_untouch_slice.m b/lib/NiftiTools/save_untouch_slice.m index dd73a752..d77d1770 100644 --- a/lib/NiftiTools/save_untouch_slice.m +++ b/lib/NiftiTools/save_untouch_slice.m @@ -3,578 +3,584 @@ % in any way, as long as their dimension is not altered. % % Usage: save_untouch_slice(slice, filename, ... -% slice_idx, [img_idx], [dim5_idx], [dim6_idx], [dim7_idx]) +% slice_idx, [img_idx], [dim5_idx], [dim6_idx], [dim7_idx]) % % slice - a portion of slices that was loaded by "load_untouch_nii". -% This should be a numeric matrix (i.e. only the .img field in the -% loaded structure) +% This should be a numeric matrix (i.e. only the .img field in the +% loaded structure) % -% filename - NIfTI or ANALYZE file name. +% filename - NIfTI or ANALYZE file name. % % slice_idx (depending on slice size) - a numerical array of image -% slice indices, which should be the same as that you entered -% in "load_untouch_nii" command. +% slice indices, which should be the same as that you entered +% in "load_untouch_nii" command. % % img_idx (depending on slice size) - a numerical array of image -% volume indices, which should be the same as that you entered -% in "load_untouch_nii" command. +% volume indices, which should be the same as that you entered +% in "load_untouch_nii" command. % -% dim5_idx (depending on slice size) - a numerical array of 5th -% dimension indices, which should be the same as that you entered -% in "load_untouch_nii" command. +% dim5_idx (depending on slice size) - a numerical array of 5th +% dimension indices, which should be the same as that you entered +% in "load_untouch_nii" command. % -% dim6_idx (depending on slice size) - a numerical array of 6th -% dimension indices, which should be the same as that you entered -% in "load_untouch_nii" command. +% dim6_idx (depending on slice size) - a numerical array of 6th +% dimension indices, which should be the same as that you entered +% in "load_untouch_nii" command. % -% dim7_idx (depending on slice size) - a numerical array of 7th -% dimension indices, which should be the same as that you entered -% in "load_untouch_nii" command. +% dim7_idx (depending on slice size) - a numerical array of 7th +% dimension indices, which should be the same as that you entered +% in "load_untouch_nii" command. % % Example: -% nii = load_nii('avg152T1_LR_nifti.nii'); -% save_nii(nii, 'test.nii'); -% view_nii(nii); -% nii = load_untouch_nii('test.nii','','','','','',[40 51:53]); -% nii.img = ones(91,109,4)*122; -% save_untouch_slice(nii.img, 'test.nii', [40 51:52]); -% nii = load_nii('test.nii'); -% view_nii(nii); +% nii = load_nii('avg152T1_LR_nifti.nii'); +% save_nii(nii, 'test.nii'); +% view_nii(nii); +% nii = load_untouch_nii('test.nii','','','','','',[40 51:53]); +% nii.img = ones(91,109,4)*122; +% save_untouch_slice(nii.img, 'test.nii', [40 51:52]); +% nii = load_nii('test.nii'); +% view_nii(nii); % % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % function save_untouch_slice(slice, filename, slice_idx, img_idx, dim5_idx, dim6_idx, dim7_idx) - if ~exist('slice','var') || ~isnumeric(slice) - msg = [char(10) '"slice" argument should be a portion of slices that was loaded' char(10)]; - msg = [msg 'by "load_untouch_nii.m". This should be a numeric matrix (i.e.' char(10)]; - msg = [msg 'only the .img field in the loaded structure).']; - error(msg); - end + if ~exist('slice', 'var') || ~isnumeric(slice) + msg = [char(10) '"slice" argument should be a portion of slices that was loaded' char(10)]; + msg = [msg 'by "load_untouch_nii.m". This should be a numeric matrix (i.e.' char(10)]; + msg = [msg 'only the .img field in the loaded structure).']; + error(msg); + end - if ~exist('filename','var') || ~exist(filename,'file') - error('In order to save back, original NIfTI or ANALYZE file must exist.'); - end + if ~exist('filename', 'var') || ~exist(filename, 'file') + error('In order to save back, original NIfTI or ANALYZE file must exist.'); + end - if ~exist('slice_idx','var') || isempty(slice_idx) || ~isequal(size(slice,3),length(slice_idx)) - msg = [char(10) '"slice_idx" is a numerical array of image slice indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end + if ~exist('slice_idx', 'var') || isempty(slice_idx) || ~isequal(size(slice, 3), length(slice_idx)) + msg = [char(10) '"slice_idx" is a numerical array of image slice indices, which' char(10)]; + msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; + msg = [msg 'command.']; + error(msg); + end - if ~exist('img_idx','var') || isempty(img_idx) - img_idx = []; + if ~exist('img_idx', 'var') || isempty(img_idx) + img_idx = []; - if ~isequal(size(slice,4),1) - msg = [char(10) '"img_idx" is a numerical array of image volume indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - elseif ~isequal(size(slice,4),length(img_idx)) + if ~isequal(size(slice, 4), 1) msg = [char(10) '"img_idx" is a numerical array of image volume indices, which' char(10)]; msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; msg = [msg 'command.']; error(msg); - end - - if ~exist('dim5_idx','var') || isempty(dim5_idx) - dim5_idx = []; - - if ~isequal(size(slice,5),1) - msg = [char(10) '"dim5_idx" is a numerical array of 5th dimension indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - elseif ~isequal(size(slice,5),length(img_idx)) - msg = [char(10) '"img_idx" is a numerical array of 5th dimension indices, which' char(10)]; + end + elseif ~isequal(size(slice, 4), length(img_idx)) + msg = [char(10) '"img_idx" is a numerical array of image volume indices, which' char(10)]; + msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; + msg = [msg 'command.']; + error(msg); + end + + if ~exist('dim5_idx', 'var') || isempty(dim5_idx) + dim5_idx = []; + + if ~isequal(size(slice, 5), 1) + msg = [char(10) '"dim5_idx" is a numerical array of 5th dimension indices, which' char(10)]; msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; msg = [msg 'command.']; error(msg); - end - - if ~exist('dim6_idx','var') || isempty(dim6_idx) - dim6_idx = []; - - if ~isequal(size(slice,6),1) - msg = [char(10) '"dim6_idx" is a numerical array of 6th dimension indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - elseif ~isequal(size(slice,6),length(img_idx)) - msg = [char(10) '"img_idx" is a numerical array of 6th dimension indices, which' char(10)]; + end + elseif ~isequal(size(slice, 5), length(img_idx)) + msg = [char(10) '"img_idx" is a numerical array of 5th dimension indices, which' char(10)]; + msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; + msg = [msg 'command.']; + error(msg); + end + + if ~exist('dim6_idx', 'var') || isempty(dim6_idx) + dim6_idx = []; + + if ~isequal(size(slice, 6), 1) + msg = [char(10) '"dim6_idx" is a numerical array of 6th dimension indices, which' char(10)]; msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; msg = [msg 'command.']; error(msg); - end - - if ~exist('dim7_idx','var') || isempty(dim7_idx) - dim7_idx = []; - - if ~isequal(size(slice,7),1) - msg = [char(10) '"dim7_idx" is a numerical array of 7th dimension indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - elseif ~isequal(size(slice,7),length(img_idx)) - msg = [char(10) '"img_idx" is a numerical array of 7th dimension indices, which' char(10)]; + end + elseif ~isequal(size(slice, 6), length(img_idx)) + msg = [char(10) '"img_idx" is a numerical array of 6th dimension indices, which' char(10)]; + msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; + msg = [msg 'command.']; + error(msg); + end + + if ~exist('dim7_idx', 'var') || isempty(dim7_idx) + dim7_idx = []; + + if ~isequal(size(slice, 7), 1) + msg = [char(10) '"dim7_idx" is a numerical array of 7th dimension indices, which' char(10)]; msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; msg = [msg 'command.']; error(msg); - end - - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 || ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - % Read the dataset header - % - [nii.hdr,nii.filetype,nii.fileprefix,nii.machine] = load_nii_hdr(filename); - - if nii.filetype == 0 - nii.hdr = load_untouch0_nii_hdr(nii.fileprefix,nii.machine); - else - nii.hdr = load_untouch_nii_hdr(nii.fileprefix,nii.machine,nii.filetype); - end - - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - - % fix fileprefix so it doesn't point to temp location - % - nii.fileprefix = gzFileName(1:end-7); -% rmdir(tmpDir,'s'); - end - - [p,f] = fileparts(filename); - fileprefix = fullfile(p, f); -% fileprefix = nii.fileprefix; - filetype = nii.filetype; - - if ~isequal( nii.hdr.dime.dim(2:3), [size(slice,1),size(slice,2)] ) - msg = [char(10) 'The first two dimensions of slice matrix should be the same as' char(10)]; - msg = [msg 'the first two dimensions of image loaded by "load_untouch_nii".']; + end + elseif ~isequal(size(slice, 7), length(img_idx)) + msg = [char(10) '"img_idx" is a numerical array of 7th dimension indices, which' char(10)]; + msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; + msg = [msg 'command.']; + error(msg); + end + + v = version; + + % Check file extension. If .gz, unpack it into temp folder + % + if length(filename) > 2 & strcmp(filename(end - 2:end), '.gz') + + if ~strcmp(filename(end - 6:end), '.img.gz') & ... + ~strcmp(filename(end - 6:end), '.hdr.gz') & ... + ~strcmp(filename(end - 6:end), '.nii.gz') + + error('Please check filename.'); + end + + if str2num(v(1:3)) < 7.1 || ~usejava('jvm') + error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); + elseif strcmp(filename(end - 6:end), '.img.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.hdr.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.hdr.gz') + filename1 = filename; + filename2 = filename; + filename2(end - 6:end) = ''; + filename2 = [filename2, '.img.gz']; + + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + + filename1 = gunzip(filename1, tmpDir); + filename2 = gunzip(filename2, tmpDir); + filename = char(filename1); % convert from cell to string + elseif strcmp(filename(end - 6:end), '.nii.gz') + tmpDir = tempname; + mkdir(tmpDir); + gzFileName = filename; + filename = gunzip(filename, tmpDir); + filename = char(filename); % convert from cell to string + end + end + + % Read the dataset header + % + [nii.hdr, nii.filetype, nii.fileprefix, nii.machine] = load_nii_hdr(filename); + + if nii.filetype == 0 + nii.hdr = load_untouch0_nii_hdr(nii.fileprefix, nii.machine); + else + nii.hdr = load_untouch_nii_hdr(nii.fileprefix, nii.machine, nii.filetype); + end + + % Clean up after gunzip + % + if exist('gzFileName', 'var') + + % fix fileprefix so it doesn't point to temp location + % + nii.fileprefix = gzFileName(1:end - 7); + % rmdir(tmpDir,'s'); + end + + [p, f] = fileparts(filename); + fileprefix = fullfile(p, f); + % fileprefix = nii.fileprefix; + filetype = nii.filetype; + + if ~isequal(nii.hdr.dime.dim(2:3), [size(slice, 1), size(slice, 2)]) + msg = [char(10) 'The first two dimensions of slice matrix should be the same as' char(10)]; + msg = [msg 'the first two dimensions of image loaded by "load_untouch_nii".']; + error(msg); + end + + % Save the dataset body + % + save_untouch_slice_img(slice, nii.hdr, filetype, fileprefix, ... + nii.machine, slice_idx, img_idx, dim5_idx, dim6_idx, dim7_idx); + + % gzip output file if requested + % + if exist('gzFileName', 'var') + [p, f] = fileparts(gzFileName); + + if filetype == 1 + gzip([fileprefix, '.img']); + delete([fileprefix, '.img']); + movefile([fileprefix, '.img.gz']); + gzip([fileprefix, '.hdr']); + delete([fileprefix, '.hdr']); + movefile([fileprefix, '.hdr.gz']); + elseif filetype == 2 + gzip([fileprefix, '.nii']); + delete([fileprefix, '.nii']); + movefile([fileprefix, '.nii.gz']); + end + + rmdir(tmpDir, 's'); + end + + return % save_untouch_slice + + % -------------------------------------------------------------------------- +function save_untouch_slice_img(slice, hdr, filetype, fileprefix, machine, slice_idx, img_idx, dim5_idx, dim6_idx, dim7_idx) + + if ~exist('hdr', 'var') || ~exist('filetype', 'var') || ~exist('fileprefix', 'var') || ~exist('machine', 'var') + error('Usage: save_untouch_slice_img(slice,hdr,filetype,fileprefix,machine,slice_idx,[img_idx],[dim5_idx],[dim6_idx],[dim7_idx]);'); + end + + if ~exist('slice_idx', 'var') || isempty(slice_idx) || hdr.dime.dim(4) < 1 + slice_idx = []; + end + + if ~exist('img_idx', 'var') || isempty(img_idx) || hdr.dime.dim(5) < 1 + img_idx = []; + end + + if ~exist('dim5_idx', 'var') || isempty(dim5_idx) || hdr.dime.dim(6) < 1 + dim5_idx = []; + end + + if ~exist('dim6_idx', 'var') || isempty(dim6_idx) || hdr.dime.dim(7) < 1 + dim6_idx = []; + end + + if ~exist('dim7_idx', 'var') || isempty(dim7_idx) || hdr.dime.dim(8) < 1 + dim7_idx = []; + end + + % check img_idx + % + if ~isempty(img_idx) & ~isnumeric(img_idx) + error('"img_idx" should be a numerical array.'); + end + + if length(unique(img_idx)) ~= length(img_idx) + error('Duplicate image index in "img_idx"'); + end + + if ~isempty(img_idx) & (min(img_idx) < 1 || max(img_idx) > hdr.dime.dim(5)) + max_range = hdr.dime.dim(5); + + if max_range == 1 + error(['"img_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"img_idx" should be an integer within the range of [' range '].']); + end + end + + % check dim5_idx + % + if ~isempty(dim5_idx) & ~isnumeric(dim5_idx) + error('"dim5_idx" should be a numerical array.'); + end + + if length(unique(dim5_idx)) ~= length(dim5_idx) + error('Duplicate index in "dim5_idx"'); + end + + if ~isempty(dim5_idx) & (min(dim5_idx) < 1 || max(dim5_idx) > hdr.dime.dim(6)) + max_range = hdr.dime.dim(6); + + if max_range == 1 + error(['"dim5_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"dim5_idx" should be an integer within the range of [' range '].']); + end + end + + % check dim6_idx + % + if ~isempty(dim6_idx) & ~isnumeric(dim6_idx) + error('"dim6_idx" should be a numerical array.'); + end + + if length(unique(dim6_idx)) ~= length(dim6_idx) + error('Duplicate index in "dim6_idx"'); + end + + if ~isempty(dim6_idx) & (min(dim6_idx) < 1 || max(dim6_idx) > hdr.dime.dim(7)) + max_range = hdr.dime.dim(7); + + if max_range == 1 + error(['"dim6_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"dim6_idx" should be an integer within the range of [' range '].']); + end + end + + % check dim7_idx + % + if ~isempty(dim7_idx) & ~isnumeric(dim7_idx) + error('"dim7_idx" should be a numerical array.'); + end + + if length(unique(dim7_idx)) ~= length(dim7_idx) + error('Duplicate index in "dim7_idx"'); + end + + if ~isempty(dim7_idx) & (min(dim7_idx) < 1 || max(dim7_idx) > hdr.dime.dim(8)) + max_range = hdr.dime.dim(8); + + if max_range == 1 + error(['"dim7_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"dim7_idx" should be an integer within the range of [' range '].']); + end + end + + % check slice_idx + % + if ~isempty(slice_idx) & ~isnumeric(slice_idx) + error('"slice_idx" should be a numerical array.'); + end + + if length(unique(slice_idx)) ~= length(slice_idx) + error('Duplicate index in "slice_idx"'); + end + + if ~isempty(slice_idx) & (min(slice_idx) < 1 || max(slice_idx) > hdr.dime.dim(4)) + max_range = hdr.dime.dim(4); + + if max_range == 1 + error(['"slice_idx" should be 1.']); + else + range = ['1 ' num2str(max_range)]; + error(['"slice_idx" should be an integer within the range of [' range '].']); + end + end + + write_image(slice, hdr, filetype, fileprefix, machine, slice_idx, img_idx, dim5_idx, dim6_idx, dim7_idx); + + return % save_untouch_slice_img + + % --------------------------------------------------------------------- +function write_image(slice, hdr, filetype, fileprefix, machine, slice_idx, img_idx, dim5_idx, dim6_idx, dim7_idx) + + if filetype == 2 + fid = fopen(sprintf('%s.nii', fileprefix), 'r+'); + + if fid < 0 + msg = sprintf('Cannot open file %s.nii.', fileprefix); error(msg); - end - - - % Save the dataset body - % - save_untouch_slice_img(slice, nii.hdr, filetype, fileprefix, ... - nii.machine, slice_idx,img_idx,dim5_idx,dim6_idx,dim7_idx); - - % gzip output file if requested - % - if exist('gzFileName', 'var') - [p,f] = fileparts(gzFileName); - - if filetype == 1 - gzip([fileprefix, '.img']); - delete([fileprefix, '.img']); - movefile([fileprefix, '.img.gz']); - gzip([fileprefix, '.hdr']); - delete([fileprefix, '.hdr']); - movefile([fileprefix, '.hdr.gz']); - elseif filetype == 2 - gzip([fileprefix, '.nii']); - delete([fileprefix, '.nii']); - movefile([fileprefix, '.nii.gz']); - end; - - rmdir(tmpDir,'s'); - end; - - return % save_untouch_slice - - -%-------------------------------------------------------------------------- -function save_untouch_slice_img(slice,hdr,filetype,fileprefix,machine,slice_idx,img_idx,dim5_idx,dim6_idx,dim7_idx) - - if ~exist('hdr','var') || ~exist('filetype','var') || ~exist('fileprefix','var') || ~exist('machine','var') - error('Usage: save_untouch_slice_img(slice,hdr,filetype,fileprefix,machine,slice_idx,[img_idx],[dim5_idx],[dim6_idx],[dim7_idx]);'); - end - - if ~exist('slice_idx','var') || isempty(slice_idx) || hdr.dime.dim(4)<1 - slice_idx = []; - end - - if ~exist('img_idx','var') || isempty(img_idx) || hdr.dime.dim(5)<1 - img_idx = []; - end - - if ~exist('dim5_idx','var') || isempty(dim5_idx) || hdr.dime.dim(6)<1 - dim5_idx = []; - end - - if ~exist('dim6_idx','var') || isempty(dim6_idx) || hdr.dime.dim(7)<1 - dim6_idx = []; - end - - if ~exist('dim7_idx','var') || isempty(dim7_idx) || hdr.dime.dim(8)<1 - dim7_idx = []; - end - - % check img_idx - % - if ~isempty(img_idx) & ~isnumeric(img_idx) - error('"img_idx" should be a numerical array.'); - end - - if length(unique(img_idx)) ~= length(img_idx) - error('Duplicate image index in "img_idx"'); - end - - if ~isempty(img_idx) & (min(img_idx) < 1 || max(img_idx) > hdr.dime.dim(5)) - max_range = hdr.dime.dim(5); - - if max_range == 1 - error(['"img_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"img_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim5_idx - % - if ~isempty(dim5_idx) & ~isnumeric(dim5_idx) - error('"dim5_idx" should be a numerical array.'); - end - - if length(unique(dim5_idx)) ~= length(dim5_idx) - error('Duplicate index in "dim5_idx"'); - end - - if ~isempty(dim5_idx) & (min(dim5_idx) < 1 || max(dim5_idx) > hdr.dime.dim(6)) - max_range = hdr.dime.dim(6); - - if max_range == 1 - error(['"dim5_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim5_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim6_idx - % - if ~isempty(dim6_idx) & ~isnumeric(dim6_idx) - error('"dim6_idx" should be a numerical array.'); - end - - if length(unique(dim6_idx)) ~= length(dim6_idx) - error('Duplicate index in "dim6_idx"'); - end - - if ~isempty(dim6_idx) & (min(dim6_idx) < 1 || max(dim6_idx) > hdr.dime.dim(7)) - max_range = hdr.dime.dim(7); - - if max_range == 1 - error(['"dim6_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim6_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim7_idx - % - if ~isempty(dim7_idx) & ~isnumeric(dim7_idx) - error('"dim7_idx" should be a numerical array.'); - end - - if length(unique(dim7_idx)) ~= length(dim7_idx) - error('Duplicate index in "dim7_idx"'); - end - - if ~isempty(dim7_idx) & (min(dim7_idx) < 1 || max(dim7_idx) > hdr.dime.dim(8)) - max_range = hdr.dime.dim(8); - - if max_range == 1 - error(['"dim7_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim7_idx" should be an integer within the range of [' range '].']); - end - end - - % check slice_idx - % - if ~isempty(slice_idx) & ~isnumeric(slice_idx) - error('"slice_idx" should be a numerical array.'); - end - - if length(unique(slice_idx)) ~= length(slice_idx) - error('Duplicate index in "slice_idx"'); - end - - if ~isempty(slice_idx) & (min(slice_idx) < 1 || max(slice_idx) > hdr.dime.dim(4)) - max_range = hdr.dime.dim(4); - - if max_range == 1 - error(['"slice_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"slice_idx" should be an integer within the range of [' range '].']); - end - end - - write_image(slice,hdr,filetype,fileprefix,machine,slice_idx,img_idx,dim5_idx,dim6_idx,dim7_idx); - - return % save_untouch_slice_img - - -%--------------------------------------------------------------------- -function write_image(slice,hdr,filetype,fileprefix,machine,slice_idx,img_idx,dim5_idx,dim6_idx,dim7_idx) - - if filetype == 2 - fid = fopen(sprintf('%s.nii',fileprefix),'r+'); - - if fid < 0, - msg = sprintf('Cannot open file %s.nii.',fileprefix); - error(msg); - end - else - fid = fopen(sprintf('%s.img',fileprefix),'r+'); + end + else + fid = fopen(sprintf('%s.img', fileprefix), 'r+'); - if fid < 0, - msg = sprintf('Cannot open file %s.img.',fileprefix); - error(msg); - end - end - - % Set bitpix according to datatype - % - % /*Acceptable values for datatype are*/ - % - % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN - % 1 Binary (ubit1, bitpix=1) % DT_BINARY - % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 - % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 - % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 - % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 - % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 - % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 - % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 - % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 - % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 - % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 - % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 - % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 - % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 - % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 - % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % - switch hdr.dime.datatype - case 2, - hdr.dime.bitpix = 8; precision = 'uint8'; - case 4, - hdr.dime.bitpix = 16; precision = 'int16'; - case 8, - hdr.dime.bitpix = 32; precision = 'int32'; - case 16, - hdr.dime.bitpix = 32; precision = 'float32'; - case 64, - hdr.dime.bitpix = 64; precision = 'float64'; - case 128, - hdr.dime.bitpix = 24; precision = 'uint8'; - case 256 - hdr.dime.bitpix = 8; precision = 'int8'; - case 511 - hdr.dime.bitpix = 96; precision = 'float32'; - case 512 - hdr.dime.bitpix = 16; precision = 'uint16'; - case 768 - hdr.dime.bitpix = 32; precision = 'uint32'; - case 1024 - hdr.dime.bitpix = 64; precision = 'int64'; - case 1280 - hdr.dime.bitpix = 64; precision = 'uint64'; - otherwise - error('This datatype is not supported'); - end - - hdr.dime.dim(find(hdr.dime.dim < 1)) = 1; - - % move pointer to the start of image block - % - switch filetype - case {0, 1} + if fid < 0 + msg = sprintf('Cannot open file %s.img.', fileprefix); + error(msg); + end + end + + % Set bitpix according to datatype + % + % /*Acceptable values for datatype are*/ + % + % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN + % 1 Binary (ubit1, bitpix=1) % DT_BINARY + % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 + % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 + % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 + % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 + % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 + % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 + % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 + % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 + % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 + % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 + % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 + % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 + % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 + % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 + % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 + % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 + % + switch hdr.dime.datatype + case 2 + hdr.dime.bitpix = 8; + precision = 'uint8'; + case 4 + hdr.dime.bitpix = 16; + precision = 'int16'; + case 8 + hdr.dime.bitpix = 32; + precision = 'int32'; + case 16 + hdr.dime.bitpix = 32; + precision = 'float32'; + case 64 + hdr.dime.bitpix = 64; + precision = 'float64'; + case 128 + hdr.dime.bitpix = 24; + precision = 'uint8'; + case 256 + hdr.dime.bitpix = 8; + precision = 'int8'; + case 511 + hdr.dime.bitpix = 96; + precision = 'float32'; + case 512 + hdr.dime.bitpix = 16; + precision = 'uint16'; + case 768 + hdr.dime.bitpix = 32; + precision = 'uint32'; + case 1024 + hdr.dime.bitpix = 64; + precision = 'int64'; + case 1280 + hdr.dime.bitpix = 64; + precision = 'uint64'; + otherwise + error('This datatype is not supported'); + end + + hdr.dime.dim(find(hdr.dime.dim < 1)) = 1; + + % move pointer to the start of image block + % + switch filetype + case {0, 1} fseek(fid, 0, 'bof'); - case 2 + case 2 fseek(fid, hdr.dime.vox_offset, 'bof'); - end - - if hdr.dime.datatype == 1 || isequal(hdr.dime.dim(4:8),ones(1,5)) || ... - (isempty(img_idx) & isempty(dim5_idx) & isempty(dim6_idx) & isempty(dim7_idx) & isempty(slice_idx)) - - msg = [char(10) char(10) ' "save_untouch_slice" is used to save back to the original image a' char(10)]; - msg = [msg ' portion of slices that were loaded by "load_untouch_nii". You can' char(10)]; - msg = [msg ' process those slices matrix in any way, as long as their dimension' char(10)]; - msg = [msg ' is not changed.']; - error(msg); - else - - d1 = hdr.dime.dim(2); - d2 = hdr.dime.dim(3); - d3 = hdr.dime.dim(4); - d4 = hdr.dime.dim(5); - d5 = hdr.dime.dim(6); - d6 = hdr.dime.dim(7); - d7 = hdr.dime.dim(8); - - if isempty(slice_idx) - slice_idx = 1:d3; - end + end + + if hdr.dime.datatype == 1 || isequal(hdr.dime.dim(4:8), ones(1, 5)) || ... + (isempty(img_idx) & isempty(dim5_idx) & isempty(dim6_idx) & isempty(dim7_idx) & isempty(slice_idx)) + + msg = [char(10) char(10) ' "save_untouch_slice" is used to save back to the original image a' char(10)]; + msg = [msg ' portion of slices that were loaded by "load_untouch_nii". You can' char(10)]; + msg = [msg ' process those slices matrix in any way, as long as their dimension' char(10)]; + msg = [msg ' is not changed.']; + error(msg); + else + + d1 = hdr.dime.dim(2); + d2 = hdr.dime.dim(3); + d3 = hdr.dime.dim(4); + d4 = hdr.dime.dim(5); + d5 = hdr.dime.dim(6); + d6 = hdr.dime.dim(7); + d7 = hdr.dime.dim(8); + + if isempty(slice_idx) + slice_idx = 1:d3; + end + + if isempty(img_idx) + img_idx = 1:d4; + end + + if isempty(dim5_idx) + dim5_idx = 1:d5; + end + + if isempty(dim6_idx) + dim6_idx = 1:d6; + end + + if isempty(dim7_idx) + dim7_idx = 1:d7; + end + + % ROMAN: begin + roman = 1; + if roman + + % compute size of one slice + % + img_siz = prod(hdr.dime.dim(2:3)); - if isempty(img_idx) - img_idx = 1:d4; + % For complex float32 or complex float64, voxel values + % include [real, imag] + % + if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 + img_siz = img_siz * 2; end - if isempty(dim5_idx) - dim5_idx = 1:d5; + % MPH: For RGB24, voxel values include 3 separate color planes + % + if hdr.dime.datatype == 128 || hdr.dime.datatype == 511 + img_siz = img_siz * 3; end - if isempty(dim6_idx) - dim6_idx = 1:d6; - end + end % if(roman) + % ROMAN: end + + for i7 = 1:length(dim7_idx) + for i6 = 1:length(dim6_idx) + for i5 = 1:length(dim5_idx) + for t = 1:length(img_idx) + for s = 1:length(slice_idx) + + % Position is seeked in bytes. To convert dimension size + % to byte storage size, hdr.dime.bitpix/8 will be + % applied. + % + pos = sub2ind([d1 d2 d3 d4 d5 d6 d7], 1, 1, slice_idx(s), ... + img_idx(t), dim5_idx(i5), dim6_idx(i6), dim7_idx(i7)) - 1; + pos = pos * hdr.dime.bitpix / 8; + + % ROMAN: begin + if roman + % do nothing + else + img_siz = prod(hdr.dime.dim(2:3)); + + % For complex float32 or complex float64, voxel values + % include [real, imag] + % + if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 + img_siz = img_siz * 2; + end + + % MPH: For RGB24, voxel values include 3 separate color planes + % + if hdr.dime.datatype == 128 || hdr.dime.datatype == 511 + img_siz = img_siz * 3; + end + end % if (roman) + % ROMAN: end + + if filetype == 2 + fseek(fid, pos + hdr.dime.vox_offset, 'bof'); + else + fseek(fid, pos, 'bof'); + end + + % For each frame, fwrite will write precision of value + % in img_siz times + % + fwrite(fid, slice(:, :, s, t, i5, i6, i7), sprintf('*%s', precision)); - if isempty(dim7_idx) - dim7_idx = 1:d7; - end - - %ROMAN: begin - roman = 1; - if(roman) - - % compute size of one slice - % - img_siz = prod(hdr.dime.dim(2:3)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 || hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - - end; %if(roman) - % ROMAN: end - - for i7=1:length(dim7_idx) - for i6=1:length(dim6_idx) - for i5=1:length(dim5_idx) - for t=1:length(img_idx) - for s=1:length(slice_idx) - - % Position is seeked in bytes. To convert dimension size - % to byte storage size, hdr.dime.bitpix/8 will be - % applied. - % - pos = sub2ind([d1 d2 d3 d4 d5 d6 d7], 1, 1, slice_idx(s), ... - img_idx(t), dim5_idx(i5),dim6_idx(i6),dim7_idx(i7)) -1; - pos = pos * hdr.dime.bitpix/8; - - % ROMAN: begin - if(roman) - % do nothing - else - img_siz = prod(hdr.dime.dim(2:3)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 || hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 || hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - end; % if (roman) - % ROMAN: end - - if filetype == 2 - fseek(fid, pos + hdr.dime.vox_offset, 'bof'); - else - fseek(fid, pos, 'bof'); - end - - % For each frame, fwrite will write precision of value - % in img_siz times - % - fwrite(fid, slice(:,:,s,t,i5,i6,i7), sprintf('*%s',precision)); - - end - end end - end + end + end end - end - - fclose(fid); + end + end - return % write_image + fclose(fid); + return % write_image diff --git a/lib/NiftiTools/unxform_nii.m b/lib/NiftiTools/unxform_nii.m index f98ef32f..3de300af 100644 --- a/lib/NiftiTools/unxform_nii.m +++ b/lib/NiftiTools/unxform_nii.m @@ -1,6 +1,6 @@ % Undo the flipping and rotations performed by xform_nii; spit back only % the raw img data block. Initial cut will only deal with 3D volumes -% strongly assume we have called xform_nii to write down the steps used +% strongly assume we have called xform_nii to write down the steps used % in xform_nii. % % Usage: a = load_nii('original_name'); @@ -13,28 +13,27 @@ % save_nii(nii.original,'newname'); % % Where, 'newname' is created with data in the same space as the -% original_name data +% original_name data % % - Jeff Gunter, 26-JUN-06 % function outblock = unxform_nii(nii, inblock) - - if isempty(nii.hdr.hist.rot_orient) - outblock=inblock; - else - [dummy unrotate_orient] = sort(nii.hdr.hist.rot_orient); - outblock = permute(inblock, unrotate_orient); - end - if ~isempty(nii.hdr.hist.flip_orient) - flip_orient = nii.hdr.hist.flip_orient(unrotate_orient); + if isempty(nii.hdr.hist.rot_orient) + outblock = inblock; + else + [dummy unrotate_orient] = sort(nii.hdr.hist.rot_orient); + outblock = permute(inblock, unrotate_orient); + end - for i = 1:3 - if flip_orient(i) - outblock = flipdim(outblock, i); - end - end - end; + if ~isempty(nii.hdr.hist.flip_orient) + flip_orient = nii.hdr.hist.flip_orient(unrotate_orient); - return; + for i = 1:3 + if flip_orient(i) + outblock = flipdim(outblock, i); + end + end + end + return diff --git a/lib/NiftiTools/verify_nii_ext.m b/lib/NiftiTools/verify_nii_ext.m index c904df41..20369ee1 100644 --- a/lib/NiftiTools/verify_nii_ext.m +++ b/lib/NiftiTools/verify_nii_ext.m @@ -19,27 +19,26 @@ % function [ext, esize_total] = verify_nii_ext(ext) - if ~isfield(ext, 'section') - error('Incorrect NIFTI header extension structure.'); - elseif ~isfield(ext, 'num_ext') - ext.num_ext = length(ext.section); - elseif ~isfield(ext, 'extension') - ext.extension = [1 0 0 0]; - end - - esize_total = 0; + if ~isfield(ext, 'section') + error('Incorrect NIFTI header extension structure.'); + elseif ~isfield(ext, 'num_ext') + ext.num_ext = length(ext.section); + elseif ~isfield(ext, 'extension') + ext.extension = [1 0 0 0]; + end - for i=1:ext.num_ext - if ~isfield(ext.section(i), 'ecode') | ~isfield(ext.section(i), 'edata') - error('Incorrect NIFTI header extension structure.'); - end + esize_total = 0; - ext.section(i).esize = ceil((length(ext.section(i).edata)+8)/16)*16; - ext.section(i).edata = ... - [ext.section(i).edata ... - zeros(1,ext.section(i).esize-length(ext.section(i).edata)-8)]; - esize_total = esize_total + ext.section(i).esize; - end + for i = 1:ext.num_ext + if ~isfield(ext.section(i), 'ecode') | ~isfield(ext.section(i), 'edata') + error('Incorrect NIFTI header extension structure.'); + end - return % verify_nii_ext + ext.section(i).esize = ceil((length(ext.section(i).edata) + 8) / 16) * 16; + ext.section(i).edata = ... + [ext.section(i).edata ... + zeros(1, ext.section(i).esize - length(ext.section(i).edata) - 8)]; + esize_total = esize_total + ext.section(i).esize; + end + return % verify_nii_ext diff --git a/lib/NiftiTools/view_nii.m b/lib/NiftiTools/view_nii.m index 45e26b59..df4d0553 100644 --- a/lib/NiftiTools/view_nii.m +++ b/lib/NiftiTools/view_nii.m @@ -1,206 +1,206 @@ -% VIEW_NII: Create or update a 3-View (Front, Top, Side) of the -% brain data that is specified by nii structure +% VIEW_NII: Create or update a 3-View (Front, Top, Side) of the +% brain data that is specified by nii structure % -% Usage: status = view_nii([h], nii, [option]) or -% status = view_nii(h, [option]) +% Usage: status = view_nii([h], nii, [option]) or +% status = view_nii(h, [option]) % % Where, h is the figure on which the 3-View will be plotted; -% nii is the brain data in NIFTI format; -% option is a struct that configures the view plotted, can be: +% nii is the brain data in NIFTI format; +% option is a struct that configures the view plotted, can be: % -% option.command = 'init' -% option.command = 'update' -% option.command = 'clearnii' -% option.command = 'updatenii' -% option.command = 'updateimg' (nii is nii.img here) +% option.command = 'init' +% option.command = 'update' +% option.command = 'clearnii' +% option.command = 'updatenii' +% option.command = 'updateimg' (nii is nii.img here) % -% option.usecolorbar = 0 | [1] -% option.usepanel = 0 | [1] -% option.usecrosshair = 0 | [1] -% option.usestretch = 0 | [1] -% option.useimagesc = 0 | [1] -% option.useinterp = [0] | 1 +% option.usecolorbar = 0 | [1] +% option.usepanel = 0 | [1] +% option.usecrosshair = 0 | [1] +% option.usestretch = 0 | [1] +% option.useimagesc = 0 | [1] +% option.useinterp = [0] | 1 % -% option.setarea = [x y w h] | [0.05 0.05 0.9 0.9] -% option.setunit = ['vox'] | 'mm' -% option.setviewpoint = [x y z] | [origin] -% option.setscanid = [t] | [1] -% option.setcrosshaircolor = [r g b] | [1 0 0] -% option.setcolorindex = From 1 to 9 (default is 2 or 3) -% option.setcolormap = (Mx3 matrix, 0 <= val <= 1) -% option.setcolorlevel = No more than 256 (default 256) -% option.sethighcolor = [] -% option.setcbarminmax = [] -% option.setvalue = [] -% option.glblocminmax = [] -% option.setbuttondown = '' -% option.setcomplex = [0] | 1 | 2 +% option.setarea = [x y w h] | [0.05 0.05 0.9 0.9] +% option.setunit = ['vox'] | 'mm' +% option.setviewpoint = [x y z] | [origin] +% option.setscanid = [t] | [1] +% option.setcrosshaircolor = [r g b] | [1 0 0] +% option.setcolorindex = From 1 to 9 (default is 2 or 3) +% option.setcolormap = (Mx3 matrix, 0 <= val <= 1) +% option.setcolorlevel = No more than 256 (default 256) +% option.sethighcolor = [] +% option.setcbarminmax = [] +% option.setvalue = [] +% option.glblocminmax = [] +% option.setbuttondown = '' +% option.setcomplex = [0] | 1 | 2 % -% Options description in detail: -% ============================== +% Options description in detail: +% ============================== % -% 1. command: A char string that can control program. +% 1. command: A char string that can control program. % -% init: If option.command='init', the program will display -% a 3-View plot on the figure specified by figure h -% or on a new figure. If there is already a 3-View -% plot on the figure, please use option.command = -% 'updatenii' (see detail below); otherwise, the -% new 3-View plot will superimpose on the old one. -% If there is no option provided, the program will -% assume that this is an initial plot. If the figure -% handle is omitted, the program knows that it is -% an initial plot. +% init: If option.command='init', the program will display +% a 3-View plot on the figure specified by figure h +% or on a new figure. If there is already a 3-View +% plot on the figure, please use option.command = +% 'updatenii' (see detail below); otherwise, the +% new 3-View plot will superimpose on the old one. +% If there is no option provided, the program will +% assume that this is an initial plot. If the figure +% handle is omitted, the program knows that it is +% an initial plot. % -% update: If there is no command specified, and a figure -% handle of the existing 3-View plot is provided, -% the program will choose option.command='update' -% to update the 3-View plot with some new option -% items. +% update: If there is no command specified, and a figure +% handle of the existing 3-View plot is provided, +% the program will choose option.command='update' +% to update the 3-View plot with some new option +% items. % -% clearnii: Clear 3-View plot on specific figure +% clearnii: Clear 3-View plot on specific figure % -% updatenii: If a new nii is going to be loaded on a fig -% that has already 3-View plot on it, use this -% command to clear existing 3-View plot, and then -% display with new nii. So, the new nii will not -% superimpose on the existing one. All options -% for 'init' can be used for 'updatenii'. +% updatenii: If a new nii is going to be loaded on a fig +% that has already 3-View plot on it, use this +% command to clear existing 3-View plot, and then +% display with new nii. So, the new nii will not +% superimpose on the existing one. All options +% for 'init' can be used for 'updatenii'. % -% updateimg: If a new 3D matrix with the same dimension -% is going to be loaded, option.command='updateimg' -% can be used as a light-weighted 'updatenii, since -% it only updates the 3 slices with new values. -% inputing argument nii should be a 3D matrix -% (nii.img) instead of nii struct. No other option -% should be used together with 'updateimg' to keep -% this command as simple as possible. +% updateimg: If a new 3D matrix with the same dimension +% is going to be loaded, option.command='updateimg' +% can be used as a light-weighted 'updatenii, since +% it only updates the 3 slices with new values. +% inputing argument nii should be a 3D matrix +% (nii.img) instead of nii struct. No other option +% should be used together with 'updateimg' to keep +% this command as simple as possible. % % -% 2. usecolorbar: If specified and usecolorbar=0, the program -% will not include the colorbar in plot area; otherwise, -% a colorbar will be included in plot area. +% 2. usecolorbar: If specified and usecolorbar=0, the program +% will not include the colorbar in plot area; otherwise, +% a colorbar will be included in plot area. % -% 3. usepanel: If specified and usepanel=0, the control panel -% at lower right cornor will be invisible; otherwise, -% it will be visible. +% 3. usepanel: If specified and usepanel=0, the control panel +% at lower right cornor will be invisible; otherwise, +% it will be visible. % -% 4. usecrosshair: If specified and usecrosshair=0, the crosshair -% will be invisible; otherwise, it will be visible. +% 4. usecrosshair: If specified and usecrosshair=0, the crosshair +% will be invisible; otherwise, it will be visible. % -% 5. usestretch: If specified and usestretch=0, the 3 slices will -% not be stretched, and will be displayed according to -% the actual voxel size; otherwise, the 3 slices will be -% stretched to the edge. +% 5. usestretch: If specified and usestretch=0, the 3 slices will +% not be stretched, and will be displayed according to +% the actual voxel size; otherwise, the 3 slices will be +% stretched to the edge. % -% 6. useimagesc: If specified and useimagesc=0, images data will -% be used directly to match the colormap (like 'image' -% command); otherwise, image data will be scaled to full -% colormap with 'imagesc' command in Matlab. +% 6. useimagesc: If specified and useimagesc=0, images data will +% be used directly to match the colormap (like 'image' +% command); otherwise, image data will be scaled to full +% colormap with 'imagesc' command in Matlab. % -% 7. useinterp: If specified and useinterp=1, the image will be -% displayed using interpolation. Otherwise, it will be -% displayed like mosaic, and each tile stands for a -% pixel. This option does not apply to 'setvalue' option -% is set. +% 7. useinterp: If specified and useinterp=1, the image will be +% displayed using interpolation. Otherwise, it will be +% displayed like mosaic, and each tile stands for a +% pixel. This option does not apply to 'setvalue' option +% is set. % % -% 8. setarea: 3-View plot will be displayed on this specific -% region. If it is not specified, program will set the -% plot area to [0.05 0.05 0.9 0.9]. +% 8. setarea: 3-View plot will be displayed on this specific +% region. If it is not specified, program will set the +% plot area to [0.05 0.05 0.9 0.9]. % -% 9. setunit: It can be specified to setunit='voxel' or 'mm' -% and the view will change the axes unit of [X Y Z] -% accordingly. +% 9. setunit: It can be specified to setunit='voxel' or 'mm' +% and the view will change the axes unit of [X Y Z] +% accordingly. % -% 10. setviewpoint: If specified, [X Y Z] values will be used -% to set the viewpoint of 3-View plot. +% 10. setviewpoint: If specified, [X Y Z] values will be used +% to set the viewpoint of 3-View plot. % -% 11. setscanid: If specified, [t] value will be used to display -% the specified image scan in NIFTI data. +% 11. setscanid: If specified, [t] value will be used to display +% the specified image scan in NIFTI data. % -% 12. setcrosshaircolor: If specified, [r g b] value will be used -% for Crosshair Color. Otherwise, red will be the default. +% 12. setcrosshaircolor: If specified, [r g b] value will be used +% for Crosshair Color. Otherwise, red will be the default. % -% 13. setcolorindex: If specified, the 3-View will choose the -% following colormap: 2 - Bipolar; 3 - Gray; 4 - Jet; -% 5 - Cool; 6 - Bone; 7 - Hot; 8 - Copper; 9 - Pink; -% If not specified, it will choose 3 - Gray if all data -% values are not less than 0; otherwise, it will choose -% 2 - Bipolar if there is value less than 0. (Contrast -% control can only apply to 3 - Gray colormap. +% 13. setcolorindex: If specified, the 3-View will choose the +% following colormap: 2 - Bipolar; 3 - Gray; 4 - Jet; +% 5 - Cool; 6 - Bone; 7 - Hot; 8 - Copper; 9 - Pink; +% If not specified, it will choose 3 - Gray if all data +% values are not less than 0; otherwise, it will choose +% 2 - Bipolar if there is value less than 0. (Contrast +% control can only apply to 3 - Gray colormap. % -% 14. setcolormap: 3-View plot will use it as a customized colormap. -% It is a 3-column matrix with value between 0 and 1. If -% using MS-Windows version of Matlab, the number of rows -% can not be more than 256, because of Matlab limitation. -% When colormap is used, setcolorlevel option will be -% disabled automatically. +% 14. setcolormap: 3-View plot will use it as a customized colormap. +% It is a 3-column matrix with value between 0 and 1. If +% using MS-Windows version of Matlab, the number of rows +% can not be more than 256, because of Matlab limitation. +% When colormap is used, setcolorlevel option will be +% disabled automatically. % -% 15. setcolorlevel: If specified (must be no more than 256, and -% cannot be used for customized colormap), row number of -% colormap will be squeezed down to this level; otherwise, -% it will assume that setcolorlevel=256. +% 15. setcolorlevel: If specified (must be no more than 256, and +% cannot be used for customized colormap), row number of +% colormap will be squeezed down to this level; otherwise, +% it will assume that setcolorlevel=256. % -% 16. sethighcolor: If specified, program will squeeze down the -% colormap, and allocate sethighcolor (an Mx3 matrix) -% to high-end portion of the colormap. The sum of M and -% setcolorlevel should be less than 256. If setcolormap -% option is used, sethighcolor will be inserted on top -% of the setcolormap, and the setcolorlevel option will -% be disabled automatically. +% 16. sethighcolor: If specified, program will squeeze down the +% colormap, and allocate sethighcolor (an Mx3 matrix) +% to high-end portion of the colormap. The sum of M and +% setcolorlevel should be less than 256. If setcolormap +% option is used, sethighcolor will be inserted on top +% of the setcolormap, and the setcolorlevel option will +% be disabled automatically. % -% 17. setcbarminmax: if specified, the [min max] will be used to -% set the min and max of the colorbar, which does not -% include any data for highcolor. +% 17. setcbarminmax: if specified, the [min max] will be used to +% set the min and max of the colorbar, which does not +% include any data for highcolor. % -% 18. setvalue: If specified, setvalue.val (with the same size as -% the source data on solution points) in the source area -% setvalue.idx will be superimposed on the current nii -% image. So, the size of setvalue.val should be equal to -% the size of setvalue.idx. To use this feature, it needs -% single or double nii structure for background image. +% 18. setvalue: If specified, setvalue.val (with the same size as +% the source data on solution points) in the source area +% setvalue.idx will be superimposed on the current nii +% image. So, the size of setvalue.val should be equal to +% the size of setvalue.idx. To use this feature, it needs +% single or double nii structure for background image. % -% 19. glblocminmax: If specified, pgm will use glblocminmax to -% calculate the colormap, instead of minmax of image. +% 19. glblocminmax: If specified, pgm will use glblocminmax to +% calculate the colormap, instead of minmax of image. % -% 20. setbuttondown: If specified, pgm will evaluate the command -% after a click or slide action is invoked to the new -% view point. +% 20. setbuttondown: If specified, pgm will evaluate the command +% after a click or slide action is invoked to the new +% view point. % -% 21. setcomplex: This option will decide how complex data to be -% displayed: 0 - Real part of complex data; 1 - Imaginary -% part of complex data; 2 - Modulus (magnitude) of complex -% data; If not specified, it will be set to 0 (Real part -% of complex data as default option. This option only apply -% when option.command is set to 'init or 'updatenii'. +% 21. setcomplex: This option will decide how complex data to be +% displayed: 0 - Real part of complex data; 1 - Imaginary +% part of complex data; 2 - Modulus (magnitude) of complex +% data; If not specified, it will be set to 0 (Real part +% of complex data as default option. This option only apply +% when option.command is set to 'init or 'updatenii'. % % -% Additional Options for 'update' command: -% ======================================= +% Additional Options for 'update' command: +% ======================================= % -% option.enablecursormove = [1] | 0 -% option.enableviewpoint = 0 | [1] -% option.enableorigin = 0 | [1] -% option.enableunit = 0 | [1] -% option.enablecrosshair = 0 | [1] -% option.enablehistogram = 0 | [1] -% option.enablecolormap = 0 | [1] -% option.enablecontrast = 0 | [1] -% option.enablebrightness = 0 | [1] -% option.enableslider = 0 | [1] -% option.enabledirlabel = 0 | [1] +% option.enablecursormove = [1] | 0 +% option.enableviewpoint = 0 | [1] +% option.enableorigin = 0 | [1] +% option.enableunit = 0 | [1] +% option.enablecrosshair = 0 | [1] +% option.enablehistogram = 0 | [1] +% option.enablecolormap = 0 | [1] +% option.enablecontrast = 0 | [1] +% option.enablebrightness = 0 | [1] +% option.enableslider = 0 | [1] +% option.enabledirlabel = 0 | [1] % % % e.g.: -% nii = load_nii('T1'); % T1.img/hdr -% view_nii(nii); +% nii = load_nii('T1'); % T1.img/hdr +% view_nii(nii); % -% or +% or % -% h = figure('unit','normal','pos', [0.18 0.08 0.64 0.85]); -% opt.setarea = [0.05 0.05 0.9 0.9]; -% view_nii(h, nii, opt); +% h = figure('unit','normal','pos', [0.18 0.08 0.64 0.85]); +% opt.setarea = [0.05 0.05 0.9 0.9]; +% view_nii(h, nii, opt); % % % Part of this file is copied and modified from: @@ -212,296 +212,296 @@ % function status = view_nii(varargin) - if nargin < 1 - error('Please check inputs using ''help view_nii'''); - end; - - nii = ''; - opt = ''; - command = ''; - - usecolorbar = []; - usepanel = []; - usecrosshair = ''; - usestretch = []; - useimagesc = []; - useinterp = []; - - setarea = []; - setunit = ''; - setviewpoint = []; - setscanid = []; - setcrosshaircolor = []; - setcolorindex = ''; - setcolormap = 'NA'; - setcolorlevel = []; - sethighcolor = 'NA'; - setcbarminmax = []; - setvalue = []; - glblocminmax = []; - setbuttondown = ''; - setcomplex = 0; - - status = []; - - if ishandle(varargin{1}) % plot on top of this figure - - fig = varargin{1}; - - if nargin < 2 - command = 'update'; % just to get 3-View status - end + if nargin < 1 + error('Please check inputs using ''help view_nii'''); + end + + nii = ''; + opt = ''; + command = ''; + + usecolorbar = []; + usepanel = []; + usecrosshair = ''; + usestretch = []; + useimagesc = []; + useinterp = []; + + setarea = []; + setunit = ''; + setviewpoint = []; + setscanid = []; + setcrosshaircolor = []; + setcolorindex = ''; + setcolormap = 'NA'; + setcolorlevel = []; + sethighcolor = 'NA'; + setcbarminmax = []; + setvalue = []; + glblocminmax = []; + setbuttondown = ''; + setcomplex = 0; + + status = []; + + if ishandle(varargin{1}) % plot on top of this figure + + fig = varargin{1}; + + if nargin < 2 + command = 'update'; % just to get 3-View status + end - if nargin == 2 - if ~isstruct(varargin{2}) - error('2nd parameter should be either nii struct or option struct'); - end + if nargin == 2 + if ~isstruct(varargin{2}) + error('2nd parameter should be either nii struct or option struct'); + end - opt = varargin{2}; + opt = varargin{2}; - if isfield(opt,'hdr') & isfield(opt,'img') - nii = opt; - elseif isfield(opt, 'command') & (strcmpi(opt.command,'init') ... - | strcmpi(opt.command,'updatenii') ... - | strcmpi(opt.command,'updateimg') ) + if isfield(opt, 'hdr') & isfield(opt, 'img') + nii = opt; + elseif isfield(opt, 'command') & (strcmpi(opt.command, 'init') ... + | strcmpi(opt.command, 'updatenii') ... + | strcmpi(opt.command, 'updateimg')) - error('Option here cannot contain "init", "updatenii", or "updateimg" comand'); - end + error('Option here cannot contain "init", "updatenii", or "updateimg" comand'); end + end - if nargin == 3 - nii = varargin{2}; - opt = varargin{3}; + if nargin == 3 + nii = varargin{2}; + opt = varargin{3}; - if ~isstruct(opt) - error('3rd parameter should be option struct'); - end + if ~isstruct(opt) + error('3rd parameter should be option struct'); + end - if ~isfield(opt,'command') | ~strcmpi(opt.command,'updateimg') - if ~isstruct(nii) | ~isfield(nii,'hdr') | ~isfield(nii,'img') - error('2nd parameter should be nii struct'); - end + if ~isfield(opt, 'command') | ~strcmpi(opt.command, 'updateimg') + if ~isstruct(nii) | ~isfield(nii, 'hdr') | ~isfield(nii, 'img') + error('2nd parameter should be nii struct'); + end - if isfield(nii,'untouch') & nii.untouch == 1 - error('Usage: please use ''load_nii.m'' to load the structure.'); - end - end + if isfield(nii, 'untouch') & nii.untouch == 1 + error('Usage: please use ''load_nii.m'' to load the structure.'); + end end + end - set(fig, 'menubar', 'none'); + set(fig, 'menubar', 'none'); - elseif ischar(varargin{1}) % call back by event + elseif ischar(varargin{1}) % call back by event - command = lower(varargin{1}); - fig = gcbf; + command = lower(varargin{1}); + fig = gcbf; - else % start nii with a new figure + else % start nii with a new figure - nii = varargin{1}; + nii = varargin{1}; - if ~isstruct(nii) | ~isfield(nii,'hdr') | ~isfield(nii,'img') - error('1st parameter should be either a figure handle or nii struct'); - end + if ~isstruct(nii) | ~isfield(nii, 'hdr') | ~isfield(nii, 'img') + error('1st parameter should be either a figure handle or nii struct'); + end - if isfield(nii,'untouch') & nii.untouch == 1 - error('Usage: please use ''load_nii.m'' to load the structure.'); - end + if isfield(nii, 'untouch') & nii.untouch == 1 + error('Usage: please use ''load_nii.m'' to load the structure.'); + end - if nargin > 1 - opt = varargin{2}; + if nargin > 1 + opt = varargin{2}; - if isfield(opt, 'command') & ~strcmpi(opt.command,'init') - error('Option here must use "init" comand'); - end + if isfield(opt, 'command') & ~strcmpi(opt.command, 'init') + error('Option here must use "init" comand'); end + end - command = 'init'; - fig = figure('unit','normal','position',[0.15 0.08 0.70 0.85]); - view_nii_menu(fig); - rri_file_menu(fig); + command = 'init'; + fig = figure('unit', 'normal', 'position', [0.15 0.08 0.70 0.85]); + view_nii_menu(fig); + rri_file_menu(fig); - end + end - if ~isempty(opt) + if ~isempty(opt) - if isfield(opt,'command') - command = lower(opt.command); - end + if isfield(opt, 'command') + command = lower(opt.command); + end - if isempty(command) - command = 'update'; - end + if isempty(command) + command = 'update'; + end - if isfield(opt,'usecolorbar') - usecolorbar = opt.usecolorbar; - end + if isfield(opt, 'usecolorbar') + usecolorbar = opt.usecolorbar; + end - if isfield(opt,'usepanel') - usepanel = opt.usepanel; - end + if isfield(opt, 'usepanel') + usepanel = opt.usepanel; + end - if isfield(opt,'usecrosshair') - usecrosshair = opt.usecrosshair; - end + if isfield(opt, 'usecrosshair') + usecrosshair = opt.usecrosshair; + end - if isfield(opt,'usestretch') - usestretch = opt.usestretch; - end + if isfield(opt, 'usestretch') + usestretch = opt.usestretch; + end - if isfield(opt,'useimagesc') - useimagesc = opt.useimagesc; - end + if isfield(opt, 'useimagesc') + useimagesc = opt.useimagesc; + end - if isfield(opt,'useinterp') - useinterp = opt.useinterp; - end + if isfield(opt, 'useinterp') + useinterp = opt.useinterp; + end - if isfield(opt,'setarea') - setarea = opt.setarea; - end + if isfield(opt, 'setarea') + setarea = opt.setarea; + end - if isfield(opt,'setunit') - setunit = opt.setunit; - end + if isfield(opt, 'setunit') + setunit = opt.setunit; + end - if isfield(opt,'setviewpoint') - setviewpoint = opt.setviewpoint; - end + if isfield(opt, 'setviewpoint') + setviewpoint = opt.setviewpoint; + end - if isfield(opt,'setscanid') - setscanid = opt.setscanid; - end + if isfield(opt, 'setscanid') + setscanid = opt.setscanid; + end - if isfield(opt,'setcrosshaircolor') - setcrosshaircolor = opt.setcrosshaircolor; + if isfield(opt, 'setcrosshaircolor') + setcrosshaircolor = opt.setcrosshaircolor; - if ~isempty(setcrosshaircolor) & (~isnumeric(setcrosshaircolor) | ~isequal(size(setcrosshaircolor),[1 3]) | min(setcrosshaircolor(:))<0 | max(setcrosshaircolor(:))>1) - error('Crosshair Color should be a 1x3 matrix with value between 0 and 1'); - end + if ~isempty(setcrosshaircolor) & (~isnumeric(setcrosshaircolor) | ~isequal(size(setcrosshaircolor), [1 3]) | min(setcrosshaircolor(:)) < 0 | max(setcrosshaircolor(:)) > 1) + error('Crosshair Color should be a 1x3 matrix with value between 0 and 1'); end + end - if isfield(opt,'setcolorindex') - setcolorindex = round(opt.setcolorindex); + if isfield(opt, 'setcolorindex') + setcolorindex = round(opt.setcolorindex); - if ~isnumeric(setcolorindex) | setcolorindex < 1 | setcolorindex > 9 - error('Colorindex should be a number between 1 and 9'); - end + if ~isnumeric(setcolorindex) | setcolorindex < 1 | setcolorindex > 9 + error('Colorindex should be a number between 1 and 9'); end + end - if isfield(opt,'setcolormap') - setcolormap = opt.setcolormap; + if isfield(opt, 'setcolormap') + setcolormap = opt.setcolormap; - if ~isempty(setcolormap) & (~isnumeric(setcolormap) | size(setcolormap,2) ~= 3 | min(setcolormap(:))<0 | max(setcolormap(:))>1) - error('Colormap should be a Mx3 matrix with value between 0 and 1'); - end + if ~isempty(setcolormap) & (~isnumeric(setcolormap) | size(setcolormap, 2) ~= 3 | min(setcolormap(:)) < 0 | max(setcolormap(:)) > 1) + error('Colormap should be a Mx3 matrix with value between 0 and 1'); end + end - if isfield(opt,'setcolorlevel') - setcolorlevel = round(opt.setcolorlevel); + if isfield(opt, 'setcolorlevel') + setcolorlevel = round(opt.setcolorlevel); - if ~isnumeric(setcolorlevel) | setcolorlevel > 256 | setcolorlevel < 1 - error('Colorlevel should be a number between 1 and 256'); - end + if ~isnumeric(setcolorlevel) | setcolorlevel > 256 | setcolorlevel < 1 + error('Colorlevel should be a number between 1 and 256'); end + end - if isfield(opt,'sethighcolor') - sethighcolor = opt.sethighcolor; + if isfield(opt, 'sethighcolor') + sethighcolor = opt.sethighcolor; - if ~isempty(sethighcolor) & (~isnumeric(sethighcolor) | size(sethighcolor,2) ~= 3 | min(sethighcolor(:))<0 | max(sethighcolor(:))>1) - error('Highcolor should be a Mx3 matrix with value between 0 and 1'); - end + if ~isempty(sethighcolor) & (~isnumeric(sethighcolor) | size(sethighcolor, 2) ~= 3 | min(sethighcolor(:)) < 0 | max(sethighcolor(:)) > 1) + error('Highcolor should be a Mx3 matrix with value between 0 and 1'); end + end - if isfield(opt,'setcbarminmax') - setcbarminmax = opt.setcbarminmax; + if isfield(opt, 'setcbarminmax') + setcbarminmax = opt.setcbarminmax; - if isempty(setcbarminmax) | ~isnumeric(setcbarminmax) | length(setcbarminmax) ~= 2 - error('Colorbar MinMax should contain 2 values: [min max]'); - end + if isempty(setcbarminmax) | ~isnumeric(setcbarminmax) | length(setcbarminmax) ~= 2 + error('Colorbar MinMax should contain 2 values: [min max]'); end + end - if isfield(opt,'setvalue') - setvalue = opt.setvalue; - - if isempty(setvalue) | ~isstruct(setvalue) | ... - ~isfield(opt.setvalue,'idx') | ~isfield(opt.setvalue,'val') - error('setvalue should be a struct contains idx and val'); - end - - if length(opt.setvalue.idx(:)) ~= length(opt.setvalue.val(:)) - error('length of idx and val fields should be the same'); - end - - if ~strcmpi(class(opt.setvalue.idx),'single') - opt.setvalue.idx = single(opt.setvalue.idx); - end + if isfield(opt, 'setvalue') + setvalue = opt.setvalue; - if ~strcmpi(class(opt.setvalue.val),'single') - opt.setvalue.val = single(opt.setvalue.val); - end + if isempty(setvalue) | ~isstruct(setvalue) | ... + ~isfield(opt.setvalue, 'idx') | ~isfield(opt.setvalue, 'val') + error('setvalue should be a struct contains idx and val'); end - if isfield(opt,'glblocminmax') - glblocminmax = opt.glblocminmax; + if length(opt.setvalue.idx(:)) ~= length(opt.setvalue.val(:)) + error('length of idx and val fields should be the same'); end - if isfield(opt,'setbuttondown') - setbuttondown = opt.setbuttondown; + if ~strcmpi(class(opt.setvalue.idx), 'single') + opt.setvalue.idx = single(opt.setvalue.idx); end - if isfield(opt,'setcomplex') - setcomplex = opt.setcomplex; + if ~strcmpi(class(opt.setvalue.val), 'single') + opt.setvalue.val = single(opt.setvalue.val); end + end - end + if isfield(opt, 'glblocminmax') + glblocminmax = opt.glblocminmax; + end + + if isfield(opt, 'setbuttondown') + setbuttondown = opt.setbuttondown; + end + + if isfield(opt, 'setcomplex') + setcomplex = opt.setcomplex; + end + + end - switch command + switch command - case {'init'} + case {'init'} - set(fig, 'InvertHardcopy','off'); - set(fig, 'PaperPositionMode','auto'); + set(fig, 'InvertHardcopy', 'off'); + set(fig, 'PaperPositionMode', 'auto'); fig = init(nii, fig, setarea, setunit, setviewpoint, setscanid, setbuttondown, ... - setcolorindex, setcolormap, setcolorlevel, sethighcolor, setcbarminmax, ... - usecolorbar, usepanel, usecrosshair, usestretch, useimagesc, useinterp, ... - setvalue, glblocminmax, setcrosshaircolor, setcomplex); + setcolorindex, setcolormap, setcolorlevel, sethighcolor, setcbarminmax, ... + usecolorbar, usepanel, usecrosshair, usestretch, useimagesc, useinterp, ... + setvalue, glblocminmax, setcrosshaircolor, setcomplex); % get status % status = get_status(fig); - case {'update'} + case {'update'} - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); h = fig; if isempty(nii_view) - error('The figure should already contain a 3-View plot.'); + error('The figure should already contain a 3-View plot.'); end if ~isempty(opt) - % Order of the following update matters. - % - update_shape(h, setarea, usecolorbar, usestretch, useimagesc); - update_useinterp(h, useinterp); - update_useimagesc(h, useimagesc); - update_usepanel(h, usepanel); - update_colorindex(h, setcolorindex); - update_colormap(h, setcolormap); - update_highcolor(h, sethighcolor, setcolorlevel); - update_cbarminmax(h, setcbarminmax); - update_unit(h, setunit); - update_viewpoint(h, setviewpoint); - update_scanid(h, setscanid); - update_buttondown(h, setbuttondown); - update_crosshaircolor(h, setcrosshaircolor); - update_usecrosshair(h, usecrosshair); - - % Enable/Disable object - % - update_enable(h, opt); + % Order of the following update matters. + % + update_shape(h, setarea, usecolorbar, usestretch, useimagesc); + update_useinterp(h, useinterp); + update_useimagesc(h, useimagesc); + update_usepanel(h, usepanel); + update_colorindex(h, setcolorindex); + update_colormap(h, setcolormap); + update_highcolor(h, sethighcolor, setcolorlevel); + update_cbarminmax(h, setcbarminmax); + update_unit(h, setunit); + update_viewpoint(h, setviewpoint); + update_scanid(h, setscanid); + update_buttondown(h, setbuttondown); + update_crosshaircolor(h, setcrosshaircolor); + update_usecrosshair(h, usecrosshair); + + % Enable/Disable object + % + update_enable(h, opt); end @@ -509,24 +509,24 @@ % status = get_status(h); - case {'updateimg'} + case {'updateimg'} - if ~exist('nii','var') - msg = sprintf('Please input a 3D matrix brain data'); - error(msg); + if ~exist('nii', 'var') + msg = sprintf('Please input a 3D matrix brain data'); + error(msg); end % Note: nii is not nii, nii should be a 3D matrix here % if ~isnumeric(nii) - msg = sprintf('2nd parameter should be a 3D matrix, not nii struct'); - error(msg); + msg = sprintf('2nd parameter should be a 3D matrix, not nii struct'); + error(msg); end - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); if isempty(nii_view) - error('The figure should already contain a 3-View plot.'); + error('The figure should already contain a 3-View plot.'); end img = nii; @@ -536,20 +536,20 @@ % status = get_status(fig); - case {'updatenii'} + case {'updatenii'} - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); if isempty(nii_view) - error('The figure should already contain a 3-View plot.'); + error('The figure should already contain a 3-View plot.'); end - if ~isstruct(nii) | ~isfield(nii,'hdr') | ~isfield(nii,'img') - error('2nd parameter should be nii struct'); + if ~isstruct(nii) | ~isfield(nii, 'hdr') | ~isfield(nii, 'img') + error('2nd parameter should be nii struct'); end - if isfield(nii,'untouch') & nii.untouch == 1 - error('Usage: please use ''load_nii.m'' to load the structure.'); + if isfield(nii, 'untouch') & nii.untouch == 1 + error('Usage: please use ''load_nii.m'' to load the structure.'); end opt.command = 'clearnii'; @@ -562,317 +562,341 @@ % status = get_status(fig); - case {'clearnii'} + case {'clearnii'} - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); handles = struct2cell(nii_view.handles); - for i=1:length(handles) - if ishandle(handles{i}) % in case already del by parent - delete(handles{i}); - end + for i = 1:length(handles) + if ishandle(handles{i}) % in case already del by parent + delete(handles{i}); + end end - rmappdata(fig,'nii_view'); - buttonmotion = get(fig,'windowbuttonmotion'); + rmappdata(fig, 'nii_view'); + buttonmotion = get(fig, 'windowbuttonmotion'); mymotion = '; view_nii(''move_cursor'');'; buttonmotion = strrep(buttonmotion, mymotion, ''); set(fig, 'windowbuttonmotion', buttonmotion); - case {'axial_image','coronal_image','sagittal_image'} + case {'axial_image', 'coronal_image', 'sagittal_image'} switch command - case 'axial_image', view = 'axi'; axi = 0; cor = 1; sag = 1; - case 'coronal_image', view = 'cor'; axi = 1; cor = 0; sag = 1; - case 'sagittal_image', view = 'sag'; axi = 1; cor = 1; sag = 0; + case 'axial_image' + view = 'axi'; + axi = 0; + cor = 1; + sag = 1; + case 'coronal_image' + view = 'cor'; + axi = 1; + cor = 0; + sag = 1; + case 'sagittal_image' + view = 'sag'; + axi = 1; + cor = 1; + sag = 0; end - nii_view = getappdata(fig,'nii_view'); - nii_view = get_slice_position(nii_view,view); + nii_view = getappdata(fig, 'nii_view'); + nii_view = get_slice_position(nii_view, view); if isfield(nii_view, 'disp') - img = nii_view.disp; + img = nii_view.disp; else - img = nii_view.nii.img; + img = nii_view.nii.img; end % CData must be double() for Matlab 6.5 for Windows % - if axi, - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) & nii_view.useinterp - Saxi = squeeze(nii_view.bgimg(:,:,nii_view.slices.axi)); - set(nii_view.handles.axial_bg,'CData',double(Saxi)'); - end - - if isfield(nii_view.handles,'axial_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Saxi = squeeze(img(:,:,nii_view.slices.axi,:,nii_view.scanid)); - Saxi = permute(Saxi, [2 1 3]); - else - Saxi = squeeze(img(:,:,nii_view.slices.axi,nii_view.scanid)); - Saxi = Saxi'; - end - - set(nii_view.handles.axial_image,'CData',double(Saxi)); - end - - if isfield(nii_view.handles,'axial_slider'), - set(nii_view.handles.axial_slider,'Value',nii_view.slices.axi); - end; - end - - if cor, - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) & nii_view.useinterp - Scor = squeeze(nii_view.bgimg(:,nii_view.slices.cor,:)); - set(nii_view.handles.coronal_bg,'CData',double(Scor)'); - end - - if isfield(nii_view.handles,'coronal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Scor = squeeze(img(:,nii_view.slices.cor,:,:,nii_view.scanid)); - Scor = permute(Scor, [2 1 3]); - else - Scor = squeeze(img(:,nii_view.slices.cor,:,nii_view.scanid)); - Scor = Scor'; - end - - set(nii_view.handles.coronal_image,'CData',double(Scor)); - end - - if isfield(nii_view.handles,'coronal_slider'), - slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; - set(nii_view.handles.coronal_slider,'Value',slider_val); - end; - end; - - if sag, - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) & nii_view.useinterp - Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag,:,:)); - set(nii_view.handles.sagittal_bg,'CData',double(Ssag)'); - end - - if isfield(nii_view.handles,'sagittal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Ssag = squeeze(img(nii_view.slices.sag,:,:,:,nii_view.scanid)); - Ssag = permute(Ssag, [2 1 3]); - else - Ssag = squeeze(img(nii_view.slices.sag,:,:,nii_view.scanid)); - Ssag = Ssag'; - end - - set(nii_view.handles.sagittal_image,'CData',double(Ssag)); - end - - if isfield(nii_view.handles,'sagittal_slider'), - set(nii_view.handles.sagittal_slider,'Value',nii_view.slices.sag); - end; - end; + if axi + if isfield(nii_view.handles, 'axial_bg') & ~isempty(nii_view.handles.axial_bg) & nii_view.useinterp + Saxi = squeeze(nii_view.bgimg(:, :, nii_view.slices.axi)); + set(nii_view.handles.axial_bg, 'CData', double(Saxi)'); + end + + if isfield(nii_view.handles, 'axial_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Saxi = squeeze(img(:, :, nii_view.slices.axi, :, nii_view.scanid)); + Saxi = permute(Saxi, [2 1 3]); + else + Saxi = squeeze(img(:, :, nii_view.slices.axi, nii_view.scanid)); + Saxi = Saxi'; + end + + set(nii_view.handles.axial_image, 'CData', double(Saxi)); + end + + if isfield(nii_view.handles, 'axial_slider') + set(nii_view.handles.axial_slider, 'Value', nii_view.slices.axi); + end + end + + if cor + if isfield(nii_view.handles, 'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) & nii_view.useinterp + Scor = squeeze(nii_view.bgimg(:, nii_view.slices.cor, :)); + set(nii_view.handles.coronal_bg, 'CData', double(Scor)'); + end + + if isfield(nii_view.handles, 'coronal_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Scor = squeeze(img(:, nii_view.slices.cor, :, :, nii_view.scanid)); + Scor = permute(Scor, [2 1 3]); + else + Scor = squeeze(img(:, nii_view.slices.cor, :, nii_view.scanid)); + Scor = Scor'; + end + + set(nii_view.handles.coronal_image, 'CData', double(Scor)); + end + + if isfield(nii_view.handles, 'coronal_slider') + slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; + set(nii_view.handles.coronal_slider, 'Value', slider_val); + end + end + + if sag + if isfield(nii_view.handles, 'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) & nii_view.useinterp + Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag, :, :)); + set(nii_view.handles.sagittal_bg, 'CData', double(Ssag)'); + end + + if isfield(nii_view.handles, 'sagittal_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Ssag = squeeze(img(nii_view.slices.sag, :, :, :, nii_view.scanid)); + Ssag = permute(Ssag, [2 1 3]); + else + Ssag = squeeze(img(nii_view.slices.sag, :, :, nii_view.scanid)); + Ssag = Ssag'; + end + + set(nii_view.handles.sagittal_image, 'CData', double(Ssag)); + end + + if isfield(nii_view.handles, 'sagittal_slider') + set(nii_view.handles.sagittal_slider, 'Value', nii_view.slices.sag); + end + end update_nii_view(nii_view); if ~isempty(nii_view.buttondown) - eval(nii_view.buttondown); + eval(nii_view.buttondown); end - - case {'axial_slider','coronal_slider','sagittal_slider'}, - + + case {'axial_slider', 'coronal_slider', 'sagittal_slider'} + switch command - case 'axial_slider', view = 'axi'; axi = 1; cor = 0; sag = 0; - case 'coronal_slider', view = 'cor'; axi = 0; cor = 1; sag = 0; - case 'sagittal_slider', view = 'sag'; axi = 0; cor = 0; sag = 1; + case 'axial_slider' + view = 'axi'; + axi = 1; + cor = 0; + sag = 0; + case 'coronal_slider' + view = 'cor'; + axi = 0; + cor = 1; + sag = 0; + case 'sagittal_slider' + view = 'sag'; + axi = 0; + cor = 0; + sag = 1; end - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); nii_view = get_slider_position(nii_view); if isfield(nii_view, 'disp') - img = nii_view.disp; + img = nii_view.disp; else - img = nii_view.nii.img; + img = nii_view.nii.img; + end + + if axi + if isfield(nii_view.handles, 'axial_bg') & ~isempty(nii_view.handles.axial_bg) & nii_view.useinterp + Saxi = squeeze(nii_view.bgimg(:, :, nii_view.slices.axi)); + set(nii_view.handles.axial_bg, 'CData', double(Saxi)'); + end + + if isfield(nii_view.handles, 'axial_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Saxi = squeeze(img(:, :, nii_view.slices.axi, :, nii_view.scanid)); + Saxi = permute(Saxi, [2 1 3]); + else + Saxi = squeeze(img(:, :, nii_view.slices.axi, nii_view.scanid)); + Saxi = Saxi'; + end + + set(nii_view.handles.axial_image, 'CData', double(Saxi)); + end + + if isfield(nii_view.handles, 'axial_slider') + set(nii_view.handles.axial_slider, 'Value', nii_view.slices.axi); + end end - if axi, - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) & nii_view.useinterp - Saxi = squeeze(nii_view.bgimg(:,:,nii_view.slices.axi)); - set(nii_view.handles.axial_bg,'CData',double(Saxi)'); - end - - if isfield(nii_view.handles,'axial_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Saxi = squeeze(img(:,:,nii_view.slices.axi,:,nii_view.scanid)); - Saxi = permute(Saxi, [2 1 3]); - else - Saxi = squeeze(img(:,:,nii_view.slices.axi,nii_view.scanid)); - Saxi = Saxi'; - end - - set(nii_view.handles.axial_image,'CData',double(Saxi)); - end - - if isfield(nii_view.handles,'axial_slider'), - set(nii_view.handles.axial_slider,'Value',nii_view.slices.axi); - end + if cor + if isfield(nii_view.handles, 'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) & nii_view.useinterp + Scor = squeeze(nii_view.bgimg(:, nii_view.slices.cor, :)); + set(nii_view.handles.coronal_bg, 'CData', double(Scor)'); + end + + if isfield(nii_view.handles, 'coronal_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Scor = squeeze(img(:, nii_view.slices.cor, :, :, nii_view.scanid)); + Scor = permute(Scor, [2 1 3]); + else + Scor = squeeze(img(:, nii_view.slices.cor, :, nii_view.scanid)); + Scor = Scor'; + end + + set(nii_view.handles.coronal_image, 'CData', double(Scor)); + end + + if isfield(nii_view.handles, 'coronal_slider') + slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; + set(nii_view.handles.coronal_slider, 'Value', slider_val); + end end - if cor, - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) & nii_view.useinterp - Scor = squeeze(nii_view.bgimg(:,nii_view.slices.cor,:)); - set(nii_view.handles.coronal_bg,'CData',double(Scor)'); - end - - if isfield(nii_view.handles,'coronal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Scor = squeeze(img(:,nii_view.slices.cor,:,:,nii_view.scanid)); - Scor = permute(Scor, [2 1 3]); - else - Scor = squeeze(img(:,nii_view.slices.cor,:,nii_view.scanid)); - Scor = Scor'; - end - - set(nii_view.handles.coronal_image,'CData',double(Scor)); - end - - if isfield(nii_view.handles,'coronal_slider'), - slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; - set(nii_view.handles.coronal_slider,'Value',slider_val); - end - end - - if sag, - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) & nii_view.useinterp - Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag,:,:)); - set(nii_view.handles.sagittal_bg,'CData',double(Ssag)'); - end - - if isfield(nii_view.handles,'sagittal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Ssag = squeeze(img(nii_view.slices.sag,:,:,:,nii_view.scanid)); - Ssag = permute(Ssag, [2 1 3]); - else - Ssag = squeeze(img(nii_view.slices.sag,:,:,nii_view.scanid)); - Ssag = Ssag'; - end - - set(nii_view.handles.sagittal_image,'CData',double(Ssag)); - end - - if isfield(nii_view.handles,'sagittal_slider'), - set(nii_view.handles.sagittal_slider,'Value',nii_view.slices.sag); - end + if sag + if isfield(nii_view.handles, 'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) & nii_view.useinterp + Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag, :, :)); + set(nii_view.handles.sagittal_bg, 'CData', double(Ssag)'); + end + + if isfield(nii_view.handles, 'sagittal_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Ssag = squeeze(img(nii_view.slices.sag, :, :, :, nii_view.scanid)); + Ssag = permute(Ssag, [2 1 3]); + else + Ssag = squeeze(img(nii_view.slices.sag, :, :, nii_view.scanid)); + Ssag = Ssag'; + end + + set(nii_view.handles.sagittal_image, 'CData', double(Ssag)); + end + + if isfield(nii_view.handles, 'sagittal_slider') + set(nii_view.handles.sagittal_slider, 'Value', nii_view.slices.sag); + end end update_nii_view(nii_view); if ~isempty(nii_view.buttondown) - eval(nii_view.buttondown); + eval(nii_view.buttondown); end - case {'impos_edit'} + case {'impos_edit'} - nii_view = getappdata(fig,'nii_view'); - impos = str2num(get(nii_view.handles.impos,'string')); + nii_view = getappdata(fig, 'nii_view'); + impos = str2num(get(nii_view.handles.impos, 'string')); if isfield(nii_view, 'disp') - img = nii_view.disp; + img = nii_view.disp; else - img = nii_view.nii.img; + img = nii_view.nii.img; end if isempty(impos) | ~all(size(impos) == [1 3]) - msg = 'Please use 3 numbers to represent X,Y and Z'; - msgbox(msg,'Error'); - return; + msg = 'Please use 3 numbers to represent X,Y and Z'; + msgbox(msg, 'Error'); + return end slices.sag = round(impos(1)); slices.cor = round(impos(2)); slices.axi = round(impos(3)); - nii_view = convert2voxel(nii_view,slices); + nii_view = convert2voxel(nii_view, slices); nii_view = check_slices(nii_view); impos(1) = nii_view.slices.sag; impos(2) = nii_view.dims(2) - nii_view.slices.cor + 1; impos(3) = nii_view.slices.axi; - if isfield(nii_view.handles,'sagittal_slider'), - set(nii_view.handles.sagittal_slider,'Value',impos(1)); + if isfield(nii_view.handles, 'sagittal_slider') + set(nii_view.handles.sagittal_slider, 'Value', impos(1)); end - if isfield(nii_view.handles,'coronal_slider'), - set(nii_view.handles.coronal_slider,'Value',impos(2)); + if isfield(nii_view.handles, 'coronal_slider') + set(nii_view.handles.coronal_slider, 'Value', impos(2)); end - if isfield(nii_view.handles,'axial_slider'), - set(nii_view.handles.axial_slider,'Value',impos(3)); + if isfield(nii_view.handles, 'axial_slider') + set(nii_view.handles.axial_slider, 'Value', impos(3)); end nii_view = get_slider_position(nii_view); update_nii_view(nii_view); - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) & nii_view.useinterp - Saxi = squeeze(nii_view.bgimg(:,:,nii_view.slices.axi)); - set(nii_view.handles.axial_bg,'CData',double(Saxi)'); + if isfield(nii_view.handles, 'axial_bg') & ~isempty(nii_view.handles.axial_bg) & nii_view.useinterp + Saxi = squeeze(nii_view.bgimg(:, :, nii_view.slices.axi)); + set(nii_view.handles.axial_bg, 'CData', double(Saxi)'); end - if isfield(nii_view.handles,'axial_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Saxi = squeeze(img(:,:,nii_view.slices.axi,:,nii_view.scanid)); - Saxi = permute(Saxi, [2 1 3]); - else - Saxi = squeeze(img(:,:,nii_view.slices.axi,nii_view.scanid)); - Saxi = Saxi'; - end + if isfield(nii_view.handles, 'axial_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Saxi = squeeze(img(:, :, nii_view.slices.axi, :, nii_view.scanid)); + Saxi = permute(Saxi, [2 1 3]); + else + Saxi = squeeze(img(:, :, nii_view.slices.axi, nii_view.scanid)); + Saxi = Saxi'; + end - set(nii_view.handles.axial_image,'CData',double(Saxi)); + set(nii_view.handles.axial_image, 'CData', double(Saxi)); end - if isfield(nii_view.handles,'axial_slider'), - set(nii_view.handles.axial_slider,'Value',nii_view.slices.axi); + if isfield(nii_view.handles, 'axial_slider') + set(nii_view.handles.axial_slider, 'Value', nii_view.slices.axi); end - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) & nii_view.useinterp - Scor = squeeze(nii_view.bgimg(:,nii_view.slices.cor,:)); - set(nii_view.handles.coronal_bg,'CData',double(Scor)'); + if isfield(nii_view.handles, 'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) & nii_view.useinterp + Scor = squeeze(nii_view.bgimg(:, nii_view.slices.cor, :)); + set(nii_view.handles.coronal_bg, 'CData', double(Scor)'); end - if isfield(nii_view.handles,'coronal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Scor = squeeze(img(:,nii_view.slices.cor,:,:,nii_view.scanid)); - Scor = permute(Scor, [2 1 3]); - else - Scor = squeeze(img(:,nii_view.slices.cor,:,nii_view.scanid)); - Scor = Scor'; - end + if isfield(nii_view.handles, 'coronal_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Scor = squeeze(img(:, nii_view.slices.cor, :, :, nii_view.scanid)); + Scor = permute(Scor, [2 1 3]); + else + Scor = squeeze(img(:, nii_view.slices.cor, :, nii_view.scanid)); + Scor = Scor'; + end - set(nii_view.handles.coronal_image,'CData',double(Scor)); + set(nii_view.handles.coronal_image, 'CData', double(Scor)); end - if isfield(nii_view.handles,'coronal_slider'), - slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; - set(nii_view.handles.coronal_slider,'Value',slider_val); + if isfield(nii_view.handles, 'coronal_slider') + slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; + set(nii_view.handles.coronal_slider, 'Value', slider_val); end - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) & nii_view.useinterp - Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag,:,:)); - set(nii_view.handles.sagittal_bg,'CData',double(Ssag)'); + if isfield(nii_view.handles, 'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) & nii_view.useinterp + Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag, :, :)); + set(nii_view.handles.sagittal_bg, 'CData', double(Ssag)'); end - if isfield(nii_view.handles,'sagittal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Ssag = squeeze(img(nii_view.slices.sag,:,:,:,nii_view.scanid)); - Ssag = permute(Ssag, [2 1 3]); - else - Ssag = squeeze(img(nii_view.slices.sag,:,:,nii_view.scanid)); - Ssag = Ssag'; - end + if isfield(nii_view.handles, 'sagittal_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Ssag = squeeze(img(nii_view.slices.sag, :, :, :, nii_view.scanid)); + Ssag = permute(Ssag, [2 1 3]); + else + Ssag = squeeze(img(nii_view.slices.sag, :, :, nii_view.scanid)); + Ssag = Ssag'; + end - set(nii_view.handles.sagittal_image,'CData',double(Ssag)); + set(nii_view.handles.sagittal_image, 'CData', double(Ssag)); end - if isfield(nii_view.handles,'sagittal_slider'), - set(nii_view.handles.sagittal_slider,'Value',nii_view.slices.sag); + if isfield(nii_view.handles, 'sagittal_slider') + set(nii_view.handles.sagittal_slider, 'Value', nii_view.slices.sag); end axes(nii_view.handles.axial_axes); @@ -880,1974 +904,1878 @@ axes(nii_view.handles.sagittal_axes); if ~isempty(nii_view.buttondown) - eval(nii_view.buttondown); + eval(nii_view.buttondown); end - case 'coordinates', + case 'coordinates' - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); set_image_value(nii_view); - case 'crosshair', + case 'crosshair' - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); - if get(nii_view.handles.xhair,'value') == 2 % off - set(nii_view.axi_xhair.lx,'visible','off'); - set(nii_view.axi_xhair.ly,'visible','off'); - set(nii_view.cor_xhair.lx,'visible','off'); - set(nii_view.cor_xhair.ly,'visible','off'); - set(nii_view.sag_xhair.lx,'visible','off'); - set(nii_view.sag_xhair.ly,'visible','off'); + if get(nii_view.handles.xhair, 'value') == 2 % off + set(nii_view.axi_xhair.lx, 'visible', 'off'); + set(nii_view.axi_xhair.ly, 'visible', 'off'); + set(nii_view.cor_xhair.lx, 'visible', 'off'); + set(nii_view.cor_xhair.ly, 'visible', 'off'); + set(nii_view.sag_xhair.lx, 'visible', 'off'); + set(nii_view.sag_xhair.ly, 'visible', 'off'); else - set(nii_view.axi_xhair.lx,'visible','on'); - set(nii_view.axi_xhair.ly,'visible','on'); - set(nii_view.cor_xhair.lx,'visible','on'); - set(nii_view.cor_xhair.ly,'visible','on'); - set(nii_view.sag_xhair.lx,'visible','on'); - set(nii_view.sag_xhair.ly,'visible','on'); - - set(nii_view.handles.axial_axes,'selected','on'); - set(nii_view.handles.axial_axes,'selected','off'); - set(nii_view.handles.coronal_axes,'selected','on'); - set(nii_view.handles.coronal_axes,'selected','off'); - set(nii_view.handles.sagittal_axes,'selected','on'); - set(nii_view.handles.sagittal_axes,'selected','off'); + set(nii_view.axi_xhair.lx, 'visible', 'on'); + set(nii_view.axi_xhair.ly, 'visible', 'on'); + set(nii_view.cor_xhair.lx, 'visible', 'on'); + set(nii_view.cor_xhair.ly, 'visible', 'on'); + set(nii_view.sag_xhair.lx, 'visible', 'on'); + set(nii_view.sag_xhair.ly, 'visible', 'on'); + + set(nii_view.handles.axial_axes, 'selected', 'on'); + set(nii_view.handles.axial_axes, 'selected', 'off'); + set(nii_view.handles.coronal_axes, 'selected', 'on'); + set(nii_view.handles.coronal_axes, 'selected', 'off'); + set(nii_view.handles.sagittal_axes, 'selected', 'on'); + set(nii_view.handles.sagittal_axes, 'selected', 'off'); end - case 'xhair_color', + case 'xhair_color' - old_color = get(gcbo,'user'); + old_color = get(gcbo, 'user'); new_color = uisetcolor(old_color); update_crosshaircolor(fig, new_color); - case {'color','contrast_def'} + case {'color', 'contrast_def'} - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); if nii_view.numscan == 1 - if get(nii_view.handles.colorindex,'value') == 2 - set(nii_view.handles.contrast,'value',128); - elseif get(nii_view.handles.colorindex,'value') == 3 - set(nii_view.handles.contrast,'value',1); - end + if get(nii_view.handles.colorindex, 'value') == 2 + set(nii_view.handles.contrast, 'value', 128); + elseif get(nii_view.handles.colorindex, 'value') == 3 + set(nii_view.handles.contrast, 'value', 1); + end end [custom_color_map, custom_colorindex] = change_colormap(fig); if strcmpi(command, 'color') - setcolorlevel = nii_view.colorlevel; + setcolorlevel = nii_view.colorlevel; - if ~isempty(custom_color_map) % isfield(nii_view, 'color_map') - setcolormap = custom_color_map; % nii_view.color_map; - else - setcolormap = []; - end + if ~isempty(custom_color_map) % isfield(nii_view, 'color_map') + setcolormap = custom_color_map; % nii_view.color_map; + else + setcolormap = []; + end - if isfield(nii_view, 'highcolor') - sethighcolor = nii_view.highcolor; - else - sethighcolor = []; - end + if isfield(nii_view, 'highcolor') + sethighcolor = nii_view.highcolor; + else + sethighcolor = []; + end - redraw_cbar(fig, setcolorlevel, setcolormap, sethighcolor); + redraw_cbar(fig, setcolorlevel, setcolormap, sethighcolor); - if nii_view.numscan == 1 & ... - (custom_colorindex < 2 | custom_colorindex > 3) - contrastopt.enablecontrast = 0; - else - contrastopt.enablecontrast = 1; - end + if nii_view.numscan == 1 & ... + (custom_colorindex < 2 | custom_colorindex > 3) + contrastopt.enablecontrast = 0; + else + contrastopt.enablecontrast = 1; + end - update_enable(fig, contrastopt); + update_enable(fig, contrastopt); end - case {'neg_color','brightness','contrast'} + case {'neg_color', 'brightness', 'contrast'} change_colormap(fig); - case {'brightness_def'} + case {'brightness_def'} - nii_view = getappdata(fig,'nii_view'); - set(nii_view.handles.brightness,'value',0); + nii_view = getappdata(fig, 'nii_view'); + set(nii_view.handles.brightness, 'value', 0); change_colormap(fig); - case 'hist_plot' + case 'hist_plot' hist_plot(fig); - case 'hist_eq' + case 'hist_eq' hist_eq(fig); - case 'move_cursor' + case 'move_cursor' move_cursor(fig); - case 'edit_change_scan' + case 'edit_change_scan' change_scan('edit_change_scan'); - case 'slider_change_scan' + case 'slider_change_scan' change_scan('slider_change_scan'); - end - - return; % view_nii + end + return % view_nii -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function fig = init(nii, fig, area, setunit, setviewpoint, setscanid, buttondown, ... - colorindex, color_map, colorlevel, highcolor, cbarminmax, ... - usecolorbar, usepanel, usecrosshair, usestretch, useimagesc, ... - useinterp, setvalue, glblocminmax, setcrosshaircolor, ... - setcomplex) - - % Support data type COMPLEX64 & COMPLEX128 - % - if nii.hdr.dime.datatype == 32 | nii.hdr.dime.datatype == 1792 - switch setcomplex, - case 0, - nii.img = real(nii.img); - case 1, - nii.img = imag(nii.img); - case 2, - if isa(nii.img, 'double') - nii.img = abs(double(nii.img)); - else - nii.img = single(abs(double(nii.img))); - end - end - end + colorindex, color_map, colorlevel, highcolor, cbarminmax, ... + usecolorbar, usepanel, usecrosshair, usestretch, useimagesc, ... + useinterp, setvalue, glblocminmax, setcrosshaircolor, ... + setcomplex) + + % Support data type COMPLEX64 & COMPLEX128 + % + if nii.hdr.dime.datatype == 32 | nii.hdr.dime.datatype == 1792 + switch setcomplex + case 0 + nii.img = real(nii.img); + case 1 + nii.img = imag(nii.img); + case 2 + if isa(nii.img, 'double') + nii.img = abs(double(nii.img)); + else + nii.img = single(abs(double(nii.img))); + end + end + end + + if isempty(area) + area = [0.05 0.05 0.9 0.9]; + end - if isempty(area) - area = [0.05 0.05 0.9 0.9]; - end + if isempty(setscanid) + setscanid = 1; + else + setscanid = round(setscanid); - if isempty(setscanid) + if setscanid < 1 setscanid = 1; - else - setscanid = round(setscanid); + end - if setscanid < 1 - setscanid = 1; - end + if setscanid > nii.hdr.dime.dim(5) + setscanid = nii.hdr.dime.dim(5); + end + end - if setscanid > nii.hdr.dime.dim(5) - setscanid = nii.hdr.dime.dim(5); - end - end + if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 + usecolorbar = 0; + elseif isempty(usecolorbar) + usecolorbar = 1; + end - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - usecolorbar = 0; - elseif isempty(usecolorbar) - usecolorbar = 1; - end + if isempty(usepanel) + usepanel = 1; + end - if isempty(usepanel) - usepanel = 1; - end + if isempty(usestretch) + usestretch = 1; + end - if isempty(usestretch) - usestretch = 1; - end + if isempty(useimagesc) + useimagesc = 1; + end - if isempty(useimagesc) - useimagesc = 1; - end + if isempty(useinterp) + useinterp = 0; + end - if isempty(useinterp) - useinterp = 0; - end + if isempty(colorindex) + tmp = min(nii.img(:, :, :, setscanid)); - if isempty(colorindex) - tmp = min(nii.img(:,:,:,setscanid)); + if min(tmp(:)) < 0 + colorindex = 2; + setcrosshaircolor = [1 1 0]; + else + colorindex = 3; + end + end + + if isempty(color_map) | ischar(color_map) + color_map = []; + else + colorindex = 1; + end + + bgimg = []; + + if ~isempty(glblocminmax) + minvalue = glblocminmax(1); + maxvalue = glblocminmax(2); + else + minvalue = nii.img(:, :, :, setscanid); + minvalue = double(minvalue(:)); + minvalue = min(minvalue(~isnan(minvalue))); + maxvalue = nii.img(:, :, :, setscanid); + maxvalue = double(maxvalue(:)); + maxvalue = max(maxvalue(~isnan(maxvalue))); + end + + if ~isempty(setvalue) + if ~isempty(glblocminmax) + minvalue = glblocminmax(1); + maxvalue = glblocminmax(2); + else + minvalue = double(min(setvalue.val)); + maxvalue = double(max(setvalue.val)); + end - if min(tmp(:)) < 0 - colorindex = 2; - setcrosshaircolor = [1 1 0]; - else - colorindex = 3; - end - end + bgimg = double(nii.img); + minbg = double(min(bgimg(:))); + maxbg = double(max(bgimg(:))); - if isempty(color_map) | ischar(color_map) - color_map = []; - else - colorindex = 1; - end + bgimg = scale_in(bgimg, minbg, maxbg, 55) + 200; % scale to 201~256 - bgimg = []; + % 56 level for brain structure + % + % highcolor = [zeros(1,3);gray(55)]; + highcolor = gray(56); + cbarminmax = [minvalue maxvalue]; - if ~isempty(glblocminmax) - minvalue = glblocminmax(1); - maxvalue = glblocminmax(2); - else - minvalue = nii.img(:,:,:,setscanid); - minvalue = double(minvalue(:)); - minvalue = min(minvalue(~isnan(minvalue))); - maxvalue = nii.img(:,:,:,setscanid); - maxvalue = double(maxvalue(:)); - maxvalue = max(maxvalue(~isnan(maxvalue))); - end + if useinterp - if ~isempty(setvalue) - if ~isempty(glblocminmax) - minvalue = glblocminmax(1); - maxvalue = glblocminmax(2); - else - minvalue = double(min(setvalue.val)); - maxvalue = double(max(setvalue.val)); - end + % scale signal data to 1~200 + % + nii.img = repmat(nan, size(nii.img)); + nii.img(setvalue.idx) = setvalue.val; - bgimg = double(nii.img); + % 200 level for source image + % + bgimg = single(scale_out(bgimg, cbarminmax(1), cbarminmax(2), 199)); + else + + bgimg(setvalue.idx) = NaN; minbg = double(min(bgimg(:))); maxbg = double(max(bgimg(:))); + bgimg(setvalue.idx) = minbg; - bgimg = scale_in(bgimg, minbg, maxbg, 55) + 200; % scale to 201~256 - - % 56 level for brain structure + % bgimg must be normalized to [201 256] % -% highcolor = [zeros(1,3);gray(55)]; - highcolor = gray(56); - cbarminmax = [minvalue maxvalue]; + bgimg = 55 * (bgimg - min(bgimg(:))) / (max(bgimg(:)) - min(bgimg(:))) + 201; + bgimg(setvalue.idx) = 0; - if useinterp - - % scale signal data to 1~200 - % - nii.img = repmat(nan, size(nii.img)); - nii.img(setvalue.idx) = setvalue.val; - - % 200 level for source image - % - bgimg = single(scale_out(bgimg, cbarminmax(1), cbarminmax(2), 199)); - else + % scale signal data to 1~200 + % + nii.img = zeros(size(nii.img)); + nii.img(setvalue.idx) = scale_in(setvalue.val, minvalue, maxvalue, 199); + nii.img = nii.img + bgimg; + bgimg = []; + nii.img = scale_out(nii.img, cbarminmax(1), cbarminmax(2), 199); - bgimg(setvalue.idx) = NaN; - minbg = double(min(bgimg(:))); - maxbg = double(max(bgimg(:))); - bgimg(setvalue.idx) = minbg; - - % bgimg must be normalized to [201 256] - % - bgimg = 55 * (bgimg-min(bgimg(:))) / (max(bgimg(:))-min(bgimg(:))) + 201; - bgimg(setvalue.idx) = 0; - - % scale signal data to 1~200 - % - nii.img = zeros(size(nii.img)); - nii.img(setvalue.idx) = scale_in(setvalue.val, minvalue, maxvalue, 199); - nii.img = nii.img + bgimg; - bgimg = []; - nii.img = scale_out(nii.img, cbarminmax(1), cbarminmax(2), 199); - - minvalue = double(nii.img(:)); - minvalue = min(minvalue(~isnan(minvalue))); - maxvalue = double(nii.img(:)); - maxvalue = max(maxvalue(~isnan(maxvalue))); - - if ~isempty(glblocminmax) % maxvalue is gray - minvalue = glblocminmax(1); - end + minvalue = double(nii.img(:)); + minvalue = min(minvalue(~isnan(minvalue))); + maxvalue = double(nii.img(:)); + maxvalue = max(maxvalue(~isnan(maxvalue))); + if ~isempty(glblocminmax) % maxvalue is gray + minvalue = glblocminmax(1); end - colorindex = 2; - setcrosshaircolor = [1 1 0]; + end - end + colorindex = 2; + setcrosshaircolor = [1 1 0]; + + end + + if isempty(highcolor) | ischar(highcolor) + highcolor = []; + num_highcolor = 0; + else + num_highcolor = size(highcolor, 1); + end + + if isempty(colorlevel) + colorlevel = 256 - num_highcolor; + end + + if usecolorbar + cbar_area = area; + cbar_area(1) = area(1) + area(3) * 0.93; + cbar_area(3) = area(3) * 0.04; + area(3) = area(3) * 0.9; % 90% used for main axes + else + cbar_area = []; + end + + % init color (gray) scaling to make sure the slice clim take the + % global clim [min(nii.img(:)) max(nii.img(:))] + % + if isempty(bgimg) + clim = [minvalue maxvalue]; + else + clim = [minvalue double(max(bgimg(:)))]; + end + + if clim(1) == clim(2) + clim(2) = clim(1) + 0.000001; + end + + if isempty(cbarminmax) + cbarminmax = [minvalue maxvalue]; + end + + xdim = size(nii.img, 1); + ydim = size(nii.img, 2); + zdim = size(nii.img, 3); + + dims = [xdim ydim zdim]; + voxel_size = abs(nii.hdr.dime.pixdim(2:4)); % vol in mm + + if any(voxel_size <= 0) + voxel_size(find(voxel_size <= 0)) = 1; + end + + origin = abs(nii.hdr.hist.originator(1:3)); + + if isempty(origin) | all(origin == 0) % according to SPM + origin = (dims + 1) / 2; + end + + origin = round(origin); + + if any(origin > dims) % simulate fMRI + origin(find(origin > dims)) = dims(find(origin > dims)); + end + + if any(origin <= 0) + origin(find(origin <= 0)) = 1; + end + + nii_view.dims = dims; + nii_view.voxel_size = voxel_size; + nii_view.origin = origin; + + nii_view.slices.sag = 1; + nii_view.slices.cor = 1; + nii_view.slices.axi = 1; + if xdim > 1 + nii_view.slices.sag = origin(1); + end + if ydim > 1 + nii_view.slices.cor = origin(2); + end + if zdim > 1 + nii_view.slices.axi = origin(3); + end + + nii_view.area = area; + nii_view.fig = fig; + nii_view.nii = nii; % image data + nii_view.bgimg = bgimg; % background + nii_view.setvalue = setvalue; + nii_view.minvalue = minvalue; + nii_view.maxvalue = maxvalue; + nii_view.numscan = nii.hdr.dime.dim(5); + nii_view.scanid = setscanid; + + Font.FontUnits = 'point'; + Font.FontSize = 12; + + % create axes for colorbar + % + [cbar_axes cbarminmax_axes] = create_cbar_axes(fig, cbar_area); + + if isempty(cbar_area) + nii_view.cbar_area = []; + else + nii_view.cbar_area = cbar_area; + end + + % create axes for top/front/side view + % + vol_size = voxel_size .* dims; + [top_ax, front_ax, side_ax] ... + = create_ax(fig, area, vol_size, usestretch); + + top_pos = get(top_ax, 'position'); + front_pos = get(front_ax, 'position'); + side_pos = get(side_ax, 'position'); + + % Sagittal Slider + % + x = side_pos(1); + y = top_pos(2) + top_pos(4); + w = side_pos(3); + h = (front_pos(2) - y) / 2; + y = y + h; + + pos = [x y w h]; + + if xdim > 1 + slider_step(1) = 1 / (xdim); + slider_step(2) = 1.00001 / (xdim); + + handles.sagittal_slider = uicontrol('Parent', fig, ... + 'Style', 'slider', 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'BackgroundColor', [0.5 0.5 0.5], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'TooltipString', 'Sagittal slice navigation', ... + 'Min', 1, 'Max', xdim, 'SliderStep', slider_step, ... + 'Value', nii_view.slices.sag, ... + 'Callback', 'view_nii(''sagittal_slider'');'); + + set(handles.sagittal_slider, 'position', pos); % linux66 + end + + % Coronal Slider + % + x = top_pos(1); + y = top_pos(2) + top_pos(4); + w = top_pos(3); + h = (front_pos(2) - y) / 2; + y = y + h; + + pos = [x y w h]; + + if ydim > 1 + slider_step(1) = 1 / (ydim); + slider_step(2) = 1.00001 / (ydim); + + slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; + + handles.coronal_slider = uicontrol('Parent', fig, ... + 'Style', 'slider', 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'BackgroundColor', [0.5 0.5 0.5], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'TooltipString', 'Coronal slice navigation', ... + 'Min', 1, 'Max', ydim, 'SliderStep', slider_step, ... + 'Value', slider_val, ... + 'Callback', 'view_nii(''coronal_slider'');'); + + set(handles.coronal_slider, 'position', pos); % linux66 + end + + % Axial Slider + % + % x = front_pos(1) + front_pos(3); + % y = front_pos(2); + % w = side_pos(1) - x; + % h = front_pos(4); + + x = top_pos(1); + y = area(2); + w = top_pos(3); + h = top_pos(2) - y; + + pos = [x y w h]; + + if zdim > 1 + slider_step(1) = 1 / (zdim); + slider_step(2) = 1.00001 / (zdim); + + handles.axial_slider = uicontrol('Parent', fig, ... + 'Style', 'slider', 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'BackgroundColor', [0.5 0.5 0.5], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'TooltipString', 'Axial slice navigation', ... + 'Min', 1, 'Max', zdim, 'SliderStep', slider_step, ... + 'Value', nii_view.slices.axi, ... + 'Callback', 'view_nii(''axial_slider'');'); + + set(handles.axial_slider, 'position', pos); % linux66 + end + + % plot info view + % + % info_pos = [side_pos([1,3]); top_pos([2,4])]; + % info_pos = info_pos(:); + gap = side_pos(1) - (top_pos(1) + top_pos(3)); + info_pos(1) = side_pos(1) + gap; + info_pos(2) = area(2); + info_pos(3) = side_pos(3) - gap; + info_pos(4) = top_pos(2) + top_pos(4) - area(2) - gap; + + num_inputline = 10; + inputline_space = info_pos(4) / num_inputline; + + % for any info_area change, update_usestretch should also be changed + + % Image Intensity Value at Cursor + % + x = info_pos(1); + y = info_pos(2); + w = info_pos(3) * 0.5; + h = inputline_space * 0.6; + + pos = [x y w h]; + + handles.Timvalcur = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'left', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', 'Value at cursor:'); + + if usepanel + set(handles.Timvalcur, 'visible', 'on'); + end + + x = x + w; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + + handles.imvalcur = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'right', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', ' '); + + if usepanel + set(handles.imvalcur, 'visible', 'on'); + end + + % Position at Cursor + % + x = info_pos(1); + y = y + inputline_space; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + + handles.Timposcur = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'left', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', '[X Y Z] at cursor:'); + + if usepanel + set(handles.Timposcur, 'visible', 'on'); + end + + x = x + w; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + + handles.imposcur = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'right', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', ' ', 'Value', [0 0 0]); + + if usepanel + set(handles.imposcur, 'visible', 'on'); + end + + % Image Intensity Value at Mouse Click + % + x = info_pos(1); + y = y + inputline_space; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + + handles.Timval = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'left', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', 'Value at crosshair:'); + + if usepanel + set(handles.Timval, 'visible', 'on'); + end + + x = x + w; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + + handles.imval = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'right', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', ' '); + + if usepanel + set(handles.imval, 'visible', 'on'); + end + + % Viewpoint Position at Mouse Click + % + x = info_pos(1); + y = y + inputline_space; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + + handles.Timpos = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'left', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', '[X Y Z] at crosshair:'); + + if usepanel + set(handles.Timpos, 'visible', 'on'); + end + + x = x + w + 0.005; + y = y - 0.008; + w = info_pos(3) * 0.5; + h = inputline_space * 0.9; + + pos = [x y w h]; + + handles.impos = uicontrol('Parent', fig, 'Style', 'edit', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'right', ... + 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'Callback', 'view_nii(''impos_edit'');', ... + 'TooltipString', 'Viewpoint Location in Axes Unit', ... + 'visible', 'off', ... + 'String', ' ', 'Value', [0 0 0]); + + if usepanel + set(handles.impos, 'visible', 'on'); + end + + % Origin Position + % + x = info_pos(1); + y = y + inputline_space * 1.2; + w = info_pos(3) * 0.5; + h = inputline_space * 0.6; + + pos = [x y w h]; + + handles.Torigin = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'left', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', '[X Y Z] at origin:'); + + if usepanel + set(handles.Torigin, 'visible', 'on'); + end + + x = x + w; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + + handles.origin = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'right', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', ' ', 'Value', [0 0 0]); + + if usepanel + set(handles.origin, 'visible', 'on'); + end + + if 0 + % Voxel Unit + % + x = info_pos(1); + y = y + inputline_space; + w = info_pos(3) * 0.5; - if isempty(highcolor) | ischar(highcolor) - highcolor = []; - num_highcolor = 0; - else - num_highcolor = size(highcolor,1); - end + pos = [x y w h]; - if isempty(colorlevel) - colorlevel = 256 - num_highcolor; - end - - if usecolorbar - cbar_area = area; - cbar_area(1) = area(1) + area(3)*0.93; - cbar_area(3) = area(3)*0.04; - area(3) = area(3)*0.9; % 90% used for main axes - else - cbar_area = []; - end - - % init color (gray) scaling to make sure the slice clim take the - % global clim [min(nii.img(:)) max(nii.img(:))] - % - if isempty(bgimg) - clim = [minvalue maxvalue]; - else - clim = [minvalue double(max(bgimg(:)))]; - end - - if clim(1) == clim(2) - clim(2) = clim(1) + 0.000001; - end - - if isempty(cbarminmax) - cbarminmax = [minvalue maxvalue]; - end - - xdim = size(nii.img, 1); - ydim = size(nii.img, 2); - zdim = size(nii.img, 3); - - dims = [xdim ydim zdim]; - voxel_size = abs(nii.hdr.dime.pixdim(2:4)); % vol in mm - - if any(voxel_size <= 0) - voxel_size(find(voxel_size <= 0)) = 1; - end - - origin = abs(nii.hdr.hist.originator(1:3)); - - if isempty(origin) | all(origin == 0) % according to SPM - origin = (dims+1)/2; - end; - - origin = round(origin); - - if any(origin > dims) % simulate fMRI - origin(find(origin > dims)) = dims(find(origin > dims)); - end - - if any(origin <= 0) - origin(find(origin <= 0)) = 1; - end - - nii_view.dims = dims; - nii_view.voxel_size = voxel_size; - nii_view.origin = origin; - - nii_view.slices.sag = 1; - nii_view.slices.cor = 1; - nii_view.slices.axi = 1; - if xdim > 1, nii_view.slices.sag = origin(1); end - if ydim > 1, nii_view.slices.cor = origin(2); end - if zdim > 1, nii_view.slices.axi = origin(3); end - - nii_view.area = area; - nii_view.fig = fig; - nii_view.nii = nii; % image data - nii_view.bgimg = bgimg; % background - nii_view.setvalue = setvalue; - nii_view.minvalue = minvalue; - nii_view.maxvalue = maxvalue; - nii_view.numscan = nii.hdr.dime.dim(5); - nii_view.scanid = setscanid; - - Font.FontUnits = 'point'; - Font.FontSize = 12; - - % create axes for colorbar - % - [cbar_axes cbarminmax_axes] = create_cbar_axes(fig, cbar_area); - - if isempty(cbar_area) - nii_view.cbar_area = []; - else - nii_view.cbar_area = cbar_area; - end - - % create axes for top/front/side view - % - vol_size = voxel_size .* dims; - [top_ax, front_ax, side_ax] ... - = create_ax(fig, area, vol_size, usestretch); - - top_pos = get(top_ax,'position'); - front_pos = get(front_ax,'position'); - side_pos = get(side_ax,'position'); - - % Sagittal Slider - % - x = side_pos(1); - y = top_pos(2) + top_pos(4); - w = side_pos(3); - h = (front_pos(2) - y) / 2; - y = y + h; - - pos = [x y w h]; - - if xdim > 1, - slider_step(1) = 1/(xdim); - slider_step(2) = 1.00001/(xdim); - - handles.sagittal_slider = uicontrol('Parent',fig, ... - 'Style','slider','Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment','center',... - 'BackgroundColor',[0.5 0.5 0.5],'ForegroundColor',[0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Sagittal slice navigation',... - 'Min',1,'Max',xdim,'SliderStep',slider_step, ... - 'Value',nii_view.slices.sag,... - 'Callback','view_nii(''sagittal_slider'');'); - - set(handles.sagittal_slider,'position',pos); % linux66 - end - - % Coronal Slider - % - x = top_pos(1); - y = top_pos(2) + top_pos(4); - w = top_pos(3); - h = (front_pos(2) - y) / 2; - y = y + h; - - pos = [x y w h]; - - if ydim > 1, - slider_step(1) = 1/(ydim); - slider_step(2) = 1.00001/(ydim); - - slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; - - handles.coronal_slider = uicontrol('Parent',fig, ... - 'Style','slider','Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment','center',... - 'BackgroundColor',[0.5 0.5 0.5],'ForegroundColor',[0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Coronal slice navigation',... - 'Min',1,'Max',ydim,'SliderStep',slider_step, ... - 'Value',slider_val,... - 'Callback','view_nii(''coronal_slider'');'); - - set(handles.coronal_slider,'position',pos); % linux66 - end - - % Axial Slider - % -% x = front_pos(1) + front_pos(3); -% y = front_pos(2); -% w = side_pos(1) - x; -% h = front_pos(4); - - x = top_pos(1); - y = area(2); - w = top_pos(3); - h = top_pos(2) - y; - - pos = [x y w h]; - - if zdim > 1, - slider_step(1) = 1/(zdim); - slider_step(2) = 1.00001/(zdim); - - handles.axial_slider = uicontrol('Parent',fig, ... - 'Style','slider','Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment','center',... - 'BackgroundColor',[0.5 0.5 0.5],'ForegroundColor',[0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Axial slice navigation',... - 'Min',1,'Max',zdim,'SliderStep',slider_step, ... - 'Value',nii_view.slices.axi,... - 'Callback','view_nii(''axial_slider'');'); - - set(handles.axial_slider,'position',pos); % linux66 - end - - % plot info view - % -% info_pos = [side_pos([1,3]); top_pos([2,4])]; -% info_pos = info_pos(:); - gap = side_pos(1)-(top_pos(1)+top_pos(3)); - info_pos(1) = side_pos(1) + gap; - info_pos(2) = area(2); - info_pos(3) = side_pos(3) - gap; - info_pos(4) = top_pos(2) + top_pos(4) - area(2) - gap; - - num_inputline = 10; - inputline_space =info_pos(4) / num_inputline; - - - % for any info_area change, update_usestretch should also be changed - - - % Image Intensity Value at Cursor - % - x = info_pos(1); - y = info_pos(2); - w = info_pos(3)*0.5; - h = inputline_space*0.6; - - pos = [x y w h]; - - handles.Timvalcur = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Value at cursor:'); - - if usepanel - set(handles.Timvalcur, 'visible', 'on'); - end - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.imvalcur = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'right',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String',' '); - - if usepanel - set(handles.imvalcur, 'visible', 'on'); - end - - % Position at Cursor - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.Timposcur = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','[X Y Z] at cursor:'); - - if usepanel - set(handles.Timposcur, 'visible', 'on'); - end - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.imposcur = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'right',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String',' ','Value',[0 0 0]); - - if usepanel - set(handles.imposcur, 'visible', 'on'); - end - - % Image Intensity Value at Mouse Click - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.Timval = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Value at crosshair:'); - - if usepanel - set(handles.Timval, 'visible', 'on'); - end - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.imval = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'right',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String',' '); - - if usepanel - set(handles.imval, 'visible', 'on'); - end - - % Viewpoint Position at Mouse Click - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.Timpos = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','[X Y Z] at crosshair:'); - - if usepanel - set(handles.Timpos, 'visible', 'on'); - end - - x = x + w + 0.005; - y = y - 0.008; - w = info_pos(3)*0.5; - h = inputline_space*0.9; - - pos = [x y w h]; - - handles.impos = uicontrol('Parent',fig,'Style','edit', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'right',... - 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'Callback','view_nii(''impos_edit'');', ... - 'TooltipString','Viewpoint Location in Axes Unit', ... - 'visible','off', ... - 'String',' ','Value',[0 0 0]); - - if usepanel - set(handles.impos, 'visible', 'on'); - end - - % Origin Position - % - x = info_pos(1); - y = y + inputline_space*1.2; - w = info_pos(3)*0.5; - h = inputline_space*0.6; - - pos = [x y w h]; - - handles.Torigin = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','[X Y Z] at origin:'); - - if usepanel - set(handles.Torigin, 'visible', 'on'); - end - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.origin = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'right',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String',' ','Value',[0 0 0]); - - if usepanel - set(handles.origin, 'visible', 'on'); - end - -if 0 - % Voxel Unit - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.Tcoord = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Axes Unit:'); - - if usepanel + handles.Tcoord = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'left', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', 'Axes Unit:'); + + if usepanel set(handles.Tcoord, 'visible', 'on'); - end + end - x = x + w + 0.005; - w = info_pos(3)*0.5 - 0.005; + x = x + w + 0.005; + w = info_pos(3) * 0.5 - 0.005; - pos = [x y w h]; + pos = [x y w h]; - Font.FontSize = 8; + Font.FontSize = 8; - handles.coord = uicontrol('Parent',fig,'Style','popupmenu', ... - 'Units','Normalized', Font, ... - 'Position',pos, ... - 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Choose Voxel or Millimeter',... - 'String',{'Voxel','Millimeter'},... - 'visible','off', ... - 'Callback','view_nii(''coordinates'');'); + handles.coord = uicontrol('Parent', fig, 'Style', 'popupmenu', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, ... + 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'TooltipString', 'Choose Voxel or Millimeter', ... + 'String', {'Voxel', 'Millimeter'}, ... + 'visible', 'off', ... + 'Callback', 'view_nii(''coordinates'');'); -% 'TooltipString','Choose Voxel, MNI or Talairach Coordinates',... -% 'String',{'Voxel','MNI (mm)','Talairach (mm)'},... + % 'TooltipString','Choose Voxel, MNI or Talairach Coordinates',... + % 'String',{'Voxel','MNI (mm)','Talairach (mm)'},... - Font.FontSize = 12; + Font.FontSize = 12; - if usepanel + if usepanel set(handles.coord, 'visible', 'on'); - end -end - - % Crosshair - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.4; - - pos = [x y w h]; - - handles.Txhair = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Crosshair:'); - - if usepanel - set(handles.Txhair, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.2; - h = inputline_space*0.7; - - pos = [x y w h]; - - Font.FontSize = 8; - - handles.xhair_color = uicontrol('Parent',fig,'Style','push', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Crosshair Color',... - 'User',[1 0 0],... - 'String','Color',... - 'visible','off', ... - 'Callback','view_nii(''xhair_color'');'); - - if usepanel - set(handles.xhair_color, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.7; - w = info_pos(3)*0.3; - - pos = [x y w h]; - - handles.xhair = uicontrol('Parent',fig,'Style','popupmenu', ... - 'Units','Normalized', Font, ... - 'Position',pos, ... - 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Display or Hide Crosshair',... - 'String',{'On','Off'},... - 'visible','off', ... - 'Callback','view_nii(''crosshair'');'); - - if usepanel - set(handles.xhair, 'visible', 'on'); - end - - % Histogram & Color - % - x = info_pos(1); - w = info_pos(3)*0.45; - h = inputline_space * 1.5; - - pos = [x, y+inputline_space*0.9, w, h]; - - handles.hist_frame = uicontrol('Parent',fig, ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'Position',pos, ... - 'visible','off', ... - 'Style','frame'); - - if usepanel -% set(handles.hist_frame, 'visible', 'on'); - end - - handles.coord_frame = uicontrol('Parent',fig, ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'Position',pos, ... - 'visible','off', ... - 'Style','frame'); - - if usepanel - set(handles.coord_frame, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.475; - w = info_pos(3)*0.525; - h = inputline_space * 1.5; - - pos = [x, y+inputline_space*0.9, w, h]; - - handles.color_frame = uicontrol('Parent',fig, ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'Position',pos, ... - 'visible','off', ... - 'Style','frame'); - - if usepanel - set(handles.color_frame, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space*1.2; - w = info_pos(3)*0.2; - h = inputline_space*0.7; - - pos = [x y w h]; - - Font.FontSize = 8; - - handles.hist_eq = uicontrol('Parent',fig,'Style','toggle', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Histogram Equalization',... - 'String','Hist EQ',... - 'visible','off', ... - 'Callback','view_nii(''hist_eq'');'); - - if usepanel -% set(handles.hist_eq, 'visible', 'on'); - end - - x = x + w; - w = info_pos(3)*0.2; - - pos = [x y w h]; - - handles.hist_plot = uicontrol('Parent',fig,'Style','push', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Histogram Plot',... - 'String','Hist Plot',... - 'visible','off', ... - 'Callback','view_nii(''hist_plot'');'); - - if usepanel -% set(handles.hist_plot, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.025; - w = info_pos(3)*0.4; - - pos = [x y w h]; - - handles.coord = uicontrol('Parent',fig,'Style','popupmenu', ... - 'Units','Normalized', Font, ... - 'Position',pos, ... - 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Choose Voxel or Millimeter',... - 'String',{'Voxel','Millimeter'},... - 'visible','off', ... - 'Callback','view_nii(''coordinates'');'); - -% 'TooltipString','Choose Voxel, MNI or Talairach Coordinates',... -% 'String',{'Voxel','MNI (mm)','Talairach (mm)'},... - - if usepanel - set(handles.coord, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.2; - - pos = [x y w h]; - - handles.neg_color = uicontrol('Parent',fig,'Style','toggle', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Negative Colormap',... - 'String','Negative',... - 'visible','off', ... - 'Callback','view_nii(''neg_color'');'); - - if usepanel - set(handles.neg_color, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.neg_color, 'enable', 'off'); - end - - x = info_pos(1) + info_pos(3)*0.7; - w = info_pos(3)*0.275; - - pos = [x y w h]; - - handles.colorindex = uicontrol('Parent',fig,'Style','popupmenu', ... - 'Units','Normalized', Font, ... - 'Position',pos, ... - 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Change Colormap',... - 'String',{'Custom','Bipolar','Gray','Jet','Cool','Bone','Hot','Copper','Pink'},... - 'value', colorindex, ... - 'visible','off', ... - 'Callback','view_nii(''color'');'); - - if usepanel - set(handles.colorindex, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.colorindex, 'enable', 'off'); - end - - x = info_pos(1) + info_pos(3)*0.1; - y = y + inputline_space; - w = info_pos(3)*0.28; - h = inputline_space*0.6; - - pos = [x y w h]; - - Font.FontSize = 8; - - handles.Thist = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Histogram'); - - handles.Tcoord = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Axes Unit'); - - if usepanel -% set(handles.Thist, 'visible', 'on'); - set(handles.Tcoord, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.60; - w = info_pos(3)*0.28; - - pos = [x y w h]; - - handles.Tcolor = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Colormap'); - - if usepanel - set(handles.Tcolor, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.Tcolor, 'enable', 'off'); - end - - % Contrast Frame - % - x = info_pos(1); - w = info_pos(3)*0.45; - h = inputline_space * 2; - - pos = [x, y+inputline_space*0.8, w, h]; - - handles.contrast_frame = uicontrol('Parent',fig, ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'Position',pos, ... - 'visible','off', ... - 'Style','frame'); - - if usepanel - set(handles.contrast_frame, 'visible', 'on'); - end - - if colorindex < 2 | colorindex > 3 - set(handles.contrast_frame, 'visible', 'off'); - end - - % Brightness Frame - % - x = info_pos(1) + info_pos(3)*0.475; - w = info_pos(3)*0.525; - - pos = [x, y+inputline_space*0.8, w, h]; - - handles.brightness_frame = uicontrol('Parent',fig, ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'Position',pos, ... - 'visible','off', ... - 'Style','frame'); - - if usepanel - set(handles.brightness_frame, 'visible', 'on'); - end - - % Contrast - % - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space; - w = info_pos(3)*0.4; - h = inputline_space*0.6; - - pos = [x y w h]; - - Font.FontSize = 12; - - slider_step(1) = 5/255; - slider_step(2) = 5.00001/255; - - handles.contrast = uicontrol('Parent',fig, ... - 'Style','slider','Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor',[0.5 0.5 0.5],'ForegroundColor',[0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Change contrast',... - 'Min',1,'Max',256,'SliderStep',slider_step, ... - 'Value',1, ... - 'visible','off', ... - 'Callback','view_nii(''contrast'');'); - - if usepanel - set(handles.contrast, 'visible', 'on'); - end - - if (nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511) & nii_view.numscan <= 1 - set(handles.contrast, 'enable', 'off'); - end - - if nii_view.numscan > 1 - set(handles.contrast, 'min', 1, 'max', nii_view.numscan, ... - 'sliderstep',[1/(nii_view.numscan-1) 1.00001/(nii_view.numscan-1)], ... - 'Callback', 'view_nii(''slider_change_scan'');'); - elseif colorindex < 2 | colorindex > 3 - set(handles.contrast, 'visible', 'off'); - elseif colorindex == 2 - set(handles.contrast,'value',128); - end - - set(handles.contrast,'position',pos); % linux66 - - % Brightness - % - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.475; - - pos = [x y w h]; - - Font.FontSize = 12; - - slider_step(1) = 1/50; - slider_step(2) = 1.00001/50; - - handles.brightness = uicontrol('Parent',fig, ... - 'Style','slider','Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor',[0.5 0.5 0.5],'ForegroundColor',[0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Change brightness',... - 'Min',-1,'Max',1,'SliderStep',slider_step, ... - 'Value',0, ... - 'visible','off', ... - 'Callback','view_nii(''brightness'');'); - - if usepanel - set(handles.brightness, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.brightness, 'enable', 'off'); - end - - set(handles.brightness,'position',pos); % linux66 - - % Contrast text/def - % - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space; - w = info_pos(3)*0.22; - - pos = [x y w h]; - - handles.Tcontrast = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Contrast:'); - - if usepanel - set(handles.Tcontrast, 'visible', 'on'); - end - - if (nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511) & nii_view.numscan <= 1 - set(handles.Tcontrast, 'enable', 'off'); - end - - if nii_view.numscan > 1 - set(handles.Tcontrast, 'string', 'Scan ID:'); - set(handles.contrast, 'TooltipString', 'Change Scan ID'); - elseif colorindex < 2 | colorindex > 3 - set(handles.Tcontrast, 'visible', 'off'); - end - - x = x + w; - w = info_pos(3)*0.18; - - pos = [x y w h]; - - Font.FontSize = 8; - - handles.contrast_def = uicontrol('Parent',fig,'Style','push', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Restore initial contrast',... - 'String','Reset',... - 'visible','off', ... - 'Callback','view_nii(''contrast_def'');'); - - if usepanel - set(handles.contrast_def, 'visible', 'on'); - end - - if (nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511) & nii_view.numscan <= 1 - set(handles.contrast_def, 'enable', 'off'); - end - - if nii_view.numscan > 1 - set(handles.contrast_def, 'style', 'edit', 'background', 'w', ... - 'TooltipString','Scan (or volume) index in the time series',... - 'string', '1', 'Callback', 'view_nii(''edit_change_scan'');'); - elseif colorindex < 2 | colorindex > 3 - set(handles.contrast_def, 'visible', 'off'); - end - - % Brightness text/def - % - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.295; - - pos = [x y w h]; - - Font.FontSize = 12; - - handles.Tbrightness = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Brightness:'); - - if usepanel - set(handles.Tbrightness, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.Tbrightness, 'enable', 'off'); - end - - x = x + w; - w = info_pos(3)*0.18; - - pos = [x y w h]; - - Font.FontSize = 8; - - handles.brightness_def = uicontrol('Parent',fig,'Style','push', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Restore initial brightness',... - 'String','Reset',... - 'visible','off', ... - 'Callback','view_nii(''brightness_def'');'); - - if usepanel - set(handles.brightness_def, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.brightness_def, 'enable', 'off'); - end - - % init image handles - % - handles.axial_image = []; - handles.coronal_image = []; - handles.sagittal_image = []; - - % plot axial view - % - if ~isempty(nii_view.bgimg) - bg_slice = squeeze(bgimg(:,:,nii_view.slices.axi)); - h1 = plot_view(fig, xdim, ydim, top_ax, bg_slice', clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - handles.axial_bg = h1; - else - handles.axial_bg = []; - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - img_slice = squeeze(nii.img(:,:,nii_view.slices.axi,:,setscanid)); - img_slice = permute(img_slice, [2 1 3]); - else - img_slice = squeeze(nii.img(:,:,nii_view.slices.axi,setscanid)); - img_slice = img_slice'; - end - h1 = plot_view(fig, xdim, ydim, top_ax, img_slice, clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - set(h1,'buttondown','view_nii(''axial_image'');'); - handles.axial_image = h1; - handles.axial_axes = top_ax; - - if size(img_slice,1) == 1 | size(img_slice,2) == 1 - set(top_ax,'visible','off'); - - if isfield(handles,'sagittal_slider') & ishandle(handles.sagittal_slider) - set(handles.sagittal_slider, 'visible', 'off'); - end - - if isfield(handles,'coronal_slider') & ishandle(handles.coronal_slider) - set(handles.coronal_slider, 'visible', 'off'); - end + end + end + + % Crosshair + % + x = info_pos(1); + y = y + inputline_space; + w = info_pos(3) * 0.4; + + pos = [x y w h]; + + handles.Txhair = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'left', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', 'Crosshair:'); + + if usepanel + set(handles.Txhair, 'visible', 'on'); + end + + x = info_pos(1) + info_pos(3) * 0.5; + w = info_pos(3) * 0.2; + h = inputline_space * 0.7; + + pos = [x y w h]; + + Font.FontSize = 8; + + handles.xhair_color = uicontrol('Parent', fig, 'Style', 'push', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'TooltipString', 'Crosshair Color', ... + 'User', [1 0 0], ... + 'String', 'Color', ... + 'visible', 'off', ... + 'Callback', 'view_nii(''xhair_color'');'); + + if usepanel + set(handles.xhair_color, 'visible', 'on'); + end + + x = info_pos(1) + info_pos(3) * 0.7; + w = info_pos(3) * 0.3; + + pos = [x y w h]; + + handles.xhair = uicontrol('Parent', fig, 'Style', 'popupmenu', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, ... + 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'TooltipString', 'Display or Hide Crosshair', ... + 'String', {'On', 'Off'}, ... + 'visible', 'off', ... + 'Callback', 'view_nii(''crosshair'');'); + + if usepanel + set(handles.xhair, 'visible', 'on'); + end + + % Histogram & Color + % + x = info_pos(1); + w = info_pos(3) * 0.45; + h = inputline_space * 1.5; + + pos = [x, y + inputline_space * 0.9, w, h]; + + handles.hist_frame = uicontrol('Parent', fig, ... + 'Units', 'normal', ... + 'BackgroundColor', [0.8 0.8 0.8], ... + 'Position', pos, ... + 'visible', 'off', ... + 'Style', 'frame'); + + if usepanel + % set(handles.hist_frame, 'visible', 'on'); + end + + handles.coord_frame = uicontrol('Parent', fig, ... + 'Units', 'normal', ... + 'BackgroundColor', [0.8 0.8 0.8], ... + 'Position', pos, ... + 'visible', 'off', ... + 'Style', 'frame'); + + if usepanel + set(handles.coord_frame, 'visible', 'on'); + end + + x = info_pos(1) + info_pos(3) * 0.475; + w = info_pos(3) * 0.525; + h = inputline_space * 1.5; + + pos = [x, y + inputline_space * 0.9, w, h]; + + handles.color_frame = uicontrol('Parent', fig, ... + 'Units', 'normal', ... + 'BackgroundColor', [0.8 0.8 0.8], ... + 'Position', pos, ... + 'visible', 'off', ... + 'Style', 'frame'); + + if usepanel + set(handles.color_frame, 'visible', 'on'); + end + + x = info_pos(1) + info_pos(3) * 0.025; + y = y + inputline_space * 1.2; + w = info_pos(3) * 0.2; + h = inputline_space * 0.7; + + pos = [x y w h]; + + Font.FontSize = 8; + + handles.hist_eq = uicontrol('Parent', fig, 'Style', 'toggle', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'TooltipString', 'Histogram Equalization', ... + 'String', 'Hist EQ', ... + 'visible', 'off', ... + 'Callback', 'view_nii(''hist_eq'');'); + + if usepanel + % set(handles.hist_eq, 'visible', 'on'); + end + + x = x + w; + w = info_pos(3) * 0.2; + + pos = [x y w h]; + + handles.hist_plot = uicontrol('Parent', fig, 'Style', 'push', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'TooltipString', 'Histogram Plot', ... + 'String', 'Hist Plot', ... + 'visible', 'off', ... + 'Callback', 'view_nii(''hist_plot'');'); + + if usepanel + % set(handles.hist_plot, 'visible', 'on'); + end + + x = info_pos(1) + info_pos(3) * 0.025; + w = info_pos(3) * 0.4; + + pos = [x y w h]; + + handles.coord = uicontrol('Parent', fig, 'Style', 'popupmenu', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, ... + 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'TooltipString', 'Choose Voxel or Millimeter', ... + 'String', {'Voxel', 'Millimeter'}, ... + 'visible', 'off', ... + 'Callback', 'view_nii(''coordinates'');'); + + % 'TooltipString','Choose Voxel, MNI or Talairach Coordinates',... + % 'String',{'Voxel','MNI (mm)','Talairach (mm)'},... + + if usepanel + set(handles.coord, 'visible', 'on'); + end + + x = info_pos(1) + info_pos(3) * 0.5; + w = info_pos(3) * 0.2; + + pos = [x y w h]; + + handles.neg_color = uicontrol('Parent', fig, 'Style', 'toggle', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'TooltipString', 'Negative Colormap', ... + 'String', 'Negative', ... + 'visible', 'off', ... + 'Callback', 'view_nii(''neg_color'');'); + + if usepanel + set(handles.neg_color, 'visible', 'on'); + end + + if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 + set(handles.neg_color, 'enable', 'off'); + end + + x = info_pos(1) + info_pos(3) * 0.7; + w = info_pos(3) * 0.275; + + pos = [x y w h]; + + handles.colorindex = uicontrol('Parent', fig, 'Style', 'popupmenu', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, ... + 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'TooltipString', 'Change Colormap', ... + 'String', {'Custom', 'Bipolar', 'Gray', 'Jet', 'Cool', 'Bone', 'Hot', 'Copper', 'Pink'}, ... + 'value', colorindex, ... + 'visible', 'off', ... + 'Callback', 'view_nii(''color'');'); + + if usepanel + set(handles.colorindex, 'visible', 'on'); + end + + if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 + set(handles.colorindex, 'enable', 'off'); + end + + x = info_pos(1) + info_pos(3) * 0.1; + y = y + inputline_space; + w = info_pos(3) * 0.28; + h = inputline_space * 0.6; + + pos = [x y w h]; + + Font.FontSize = 8; + + handles.Thist = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', 'Histogram'); + + handles.Tcoord = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', 'Axes Unit'); + + if usepanel + % set(handles.Thist, 'visible', 'on'); + set(handles.Tcoord, 'visible', 'on'); + end + + x = info_pos(1) + info_pos(3) * 0.60; + w = info_pos(3) * 0.28; + + pos = [x y w h]; + + handles.Tcolor = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', 'Colormap'); + + if usepanel + set(handles.Tcolor, 'visible', 'on'); + end + + if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 + set(handles.Tcolor, 'enable', 'off'); + end + + % Contrast Frame + % + x = info_pos(1); + w = info_pos(3) * 0.45; + h = inputline_space * 2; + + pos = [x, y + inputline_space * 0.8, w, h]; + + handles.contrast_frame = uicontrol('Parent', fig, ... + 'Units', 'normal', ... + 'BackgroundColor', [0.8 0.8 0.8], ... + 'Position', pos, ... + 'visible', 'off', ... + 'Style', 'frame'); + + if usepanel + set(handles.contrast_frame, 'visible', 'on'); + end + + if colorindex < 2 | colorindex > 3 + set(handles.contrast_frame, 'visible', 'off'); + end + + % Brightness Frame + % + x = info_pos(1) + info_pos(3) * 0.475; + w = info_pos(3) * 0.525; + + pos = [x, y + inputline_space * 0.8, w, h]; + + handles.brightness_frame = uicontrol('Parent', fig, ... + 'Units', 'normal', ... + 'BackgroundColor', [0.8 0.8 0.8], ... + 'Position', pos, ... + 'visible', 'off', ... + 'Style', 'frame'); + + if usepanel + set(handles.brightness_frame, 'visible', 'on'); + end + + % Contrast + % + x = info_pos(1) + info_pos(3) * 0.025; + y = y + inputline_space; + w = info_pos(3) * 0.4; + h = inputline_space * 0.6; + + pos = [x y w h]; + + Font.FontSize = 12; + + slider_step(1) = 5 / 255; + slider_step(2) = 5.00001 / 255; + + handles.contrast = uicontrol('Parent', fig, ... + 'Style', 'slider', 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'left', ... + 'BackgroundColor', [0.5 0.5 0.5], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'TooltipString', 'Change contrast', ... + 'Min', 1, 'Max', 256, 'SliderStep', slider_step, ... + 'Value', 1, ... + 'visible', 'off', ... + 'Callback', 'view_nii(''contrast'');'); + + if usepanel + set(handles.contrast, 'visible', 'on'); + end + + if (nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511) & nii_view.numscan <= 1 + set(handles.contrast, 'enable', 'off'); + end + + if nii_view.numscan > 1 + set(handles.contrast, 'min', 1, 'max', nii_view.numscan, ... + 'sliderstep', [1 / (nii_view.numscan - 1) 1.00001 / (nii_view.numscan - 1)], ... + 'Callback', 'view_nii(''slider_change_scan'');'); + elseif colorindex < 2 | colorindex > 3 + set(handles.contrast, 'visible', 'off'); + elseif colorindex == 2 + set(handles.contrast, 'value', 128); + end + + set(handles.contrast, 'position', pos); % linux66 + + % Brightness + % + x = info_pos(1) + info_pos(3) * 0.5; + w = info_pos(3) * 0.475; + + pos = [x y w h]; + + Font.FontSize = 12; + + slider_step(1) = 1 / 50; + slider_step(2) = 1.00001 / 50; + + handles.brightness = uicontrol('Parent', fig, ... + 'Style', 'slider', 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'left', ... + 'BackgroundColor', [0.5 0.5 0.5], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'TooltipString', 'Change brightness', ... + 'Min', -1, 'Max', 1, 'SliderStep', slider_step, ... + 'Value', 0, ... + 'visible', 'off', ... + 'Callback', 'view_nii(''brightness'');'); + + if usepanel + set(handles.brightness, 'visible', 'on'); + end + + if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 + set(handles.brightness, 'enable', 'off'); + end + + set(handles.brightness, 'position', pos); % linux66 + + % Contrast text/def + % + x = info_pos(1) + info_pos(3) * 0.025; + y = y + inputline_space; + w = info_pos(3) * 0.22; + + pos = [x y w h]; + + handles.Tcontrast = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'left', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', 'Contrast:'); + + if usepanel + set(handles.Tcontrast, 'visible', 'on'); + end + + if (nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511) & nii_view.numscan <= 1 + set(handles.Tcontrast, 'enable', 'off'); + end + + if nii_view.numscan > 1 + set(handles.Tcontrast, 'string', 'Scan ID:'); + set(handles.contrast, 'TooltipString', 'Change Scan ID'); + elseif colorindex < 2 | colorindex > 3 + set(handles.Tcontrast, 'visible', 'off'); + end + + x = x + w; + w = info_pos(3) * 0.18; + + pos = [x y w h]; + + Font.FontSize = 8; + + handles.contrast_def = uicontrol('Parent', fig, 'Style', 'push', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'TooltipString', 'Restore initial contrast', ... + 'String', 'Reset', ... + 'visible', 'off', ... + 'Callback', 'view_nii(''contrast_def'');'); + + if usepanel + set(handles.contrast_def, 'visible', 'on'); + end + + if (nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511) & nii_view.numscan <= 1 + set(handles.contrast_def, 'enable', 'off'); + end + + if nii_view.numscan > 1 + set(handles.contrast_def, 'style', 'edit', 'background', 'w', ... + 'TooltipString', 'Scan (or volume) index in the time series', ... + 'string', '1', 'Callback', 'view_nii(''edit_change_scan'');'); + elseif colorindex < 2 | colorindex > 3 + set(handles.contrast_def, 'visible', 'off'); + end + + % Brightness text/def + % + x = info_pos(1) + info_pos(3) * 0.5; + w = info_pos(3) * 0.295; + + pos = [x y w h]; + + Font.FontSize = 12; + + handles.Tbrightness = uicontrol('Parent', fig, 'Style', 'text', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'left', ... + 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0], ... + 'BusyAction', 'queue', ... + 'visible', 'off', ... + 'String', 'Brightness:'); + + if usepanel + set(handles.Tbrightness, 'visible', 'on'); + end + + if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 + set(handles.Tbrightness, 'enable', 'off'); + end + + x = x + w; + w = info_pos(3) * 0.18; + + pos = [x y w h]; + + Font.FontSize = 8; + + handles.brightness_def = uicontrol('Parent', fig, 'Style', 'push', ... + 'Units', 'Normalized', Font, ... + 'Position', pos, 'HorizontalAlignment', 'center', ... + 'TooltipString', 'Restore initial brightness', ... + 'String', 'Reset', ... + 'visible', 'off', ... + 'Callback', 'view_nii(''brightness_def'');'); + + if usepanel + set(handles.brightness_def, 'visible', 'on'); + end + + if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 + set(handles.brightness_def, 'enable', 'off'); + end + + % init image handles + % + handles.axial_image = []; + handles.coronal_image = []; + handles.sagittal_image = []; + + % plot axial view + % + if ~isempty(nii_view.bgimg) + bg_slice = squeeze(bgimg(:, :, nii_view.slices.axi)); + h1 = plot_view(fig, xdim, ydim, top_ax, bg_slice', clim, cbarminmax, ... + handles, useimagesc, colorindex, color_map, ... + colorlevel, highcolor, useinterp, nii_view.numscan); + handles.axial_bg = h1; + else + handles.axial_bg = []; + end + + if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 + img_slice = squeeze(nii.img(:, :, nii_view.slices.axi, :, setscanid)); + img_slice = permute(img_slice, [2 1 3]); + else + img_slice = squeeze(nii.img(:, :, nii_view.slices.axi, setscanid)); + img_slice = img_slice'; + end + h1 = plot_view(fig, xdim, ydim, top_ax, img_slice, clim, cbarminmax, ... + handles, useimagesc, colorindex, color_map, ... + colorlevel, highcolor, useinterp, nii_view.numscan); + set(h1, 'buttondown', 'view_nii(''axial_image'');'); + handles.axial_image = h1; + handles.axial_axes = top_ax; + + if size(img_slice, 1) == 1 | size(img_slice, 2) == 1 + set(top_ax, 'visible', 'off'); + + if isfield(handles, 'sagittal_slider') & ishandle(handles.sagittal_slider) + set(handles.sagittal_slider, 'visible', 'off'); + end - if isfield(handles,'axial_slider') & ishandle(handles.axial_slider) - set(handles.axial_slider, 'visible', 'off'); - end - end - - % plot coronal view - % - if ~isempty(nii_view.bgimg) - bg_slice = squeeze(bgimg(:,nii_view.slices.cor,:)); - h1 = plot_view(fig, xdim, zdim, front_ax, bg_slice', clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - handles.coronal_bg = h1; - else - handles.coronal_bg = []; - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - img_slice = squeeze(nii.img(:,nii_view.slices.cor,:,:,setscanid)); - img_slice = permute(img_slice, [2 1 3]); - else - img_slice = squeeze(nii.img(:,nii_view.slices.cor,:,setscanid)); - img_slice = img_slice'; - end - h1 = plot_view(fig, xdim, zdim, front_ax, img_slice, clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - set(h1,'buttondown','view_nii(''coronal_image'');'); - handles.coronal_image = h1; - handles.coronal_axes = front_ax; - - if size(img_slice,1) == 1 | size(img_slice,2) == 1 - set(front_ax,'visible','off'); - - if isfield(handles,'sagittal_slider') & ishandle(handles.sagittal_slider) - set(handles.sagittal_slider, 'visible', 'off'); - end + if isfield(handles, 'coronal_slider') & ishandle(handles.coronal_slider) + set(handles.coronal_slider, 'visible', 'off'); + end - if isfield(handles,'coronal_slider') & ishandle(handles.coronal_slider) - set(handles.coronal_slider, 'visible', 'off'); - end + if isfield(handles, 'axial_slider') & ishandle(handles.axial_slider) + set(handles.axial_slider, 'visible', 'off'); + end + end + + % plot coronal view + % + if ~isempty(nii_view.bgimg) + bg_slice = squeeze(bgimg(:, nii_view.slices.cor, :)); + h1 = plot_view(fig, xdim, zdim, front_ax, bg_slice', clim, cbarminmax, ... + handles, useimagesc, colorindex, color_map, ... + colorlevel, highcolor, useinterp, nii_view.numscan); + handles.coronal_bg = h1; + else + handles.coronal_bg = []; + end + + if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 + img_slice = squeeze(nii.img(:, nii_view.slices.cor, :, :, setscanid)); + img_slice = permute(img_slice, [2 1 3]); + else + img_slice = squeeze(nii.img(:, nii_view.slices.cor, :, setscanid)); + img_slice = img_slice'; + end + h1 = plot_view(fig, xdim, zdim, front_ax, img_slice, clim, cbarminmax, ... + handles, useimagesc, colorindex, color_map, ... + colorlevel, highcolor, useinterp, nii_view.numscan); + set(h1, 'buttondown', 'view_nii(''coronal_image'');'); + handles.coronal_image = h1; + handles.coronal_axes = front_ax; + + if size(img_slice, 1) == 1 | size(img_slice, 2) == 1 + set(front_ax, 'visible', 'off'); + + if isfield(handles, 'sagittal_slider') & ishandle(handles.sagittal_slider) + set(handles.sagittal_slider, 'visible', 'off'); + end - if isfield(handles,'axial_slider') & ishandle(handles.axial_slider) - set(handles.axial_slider, 'visible', 'off'); - end - end - - % plot sagittal view - % - if ~isempty(nii_view.bgimg) - bg_slice = squeeze(bgimg(nii_view.slices.sag,:,:)); - - h1 = plot_view(fig, ydim, zdim, side_ax, bg_slice', clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - handles.sagittal_bg = h1; - else - handles.sagittal_bg = []; - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - img_slice = squeeze(nii.img(nii_view.slices.sag,:,:,:,setscanid)); - img_slice = permute(img_slice, [2 1 3]); - else - img_slice = squeeze(nii.img(nii_view.slices.sag,:,:,setscanid)); - img_slice = img_slice'; - end - - h1 = plot_view(fig, ydim, zdim, side_ax, img_slice, clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - set(h1,'buttondown','view_nii(''sagittal_image'');'); - set(side_ax,'Xdir', 'reverse'); - handles.sagittal_image = h1; - handles.sagittal_axes = side_ax; - - if size(img_slice,1) == 1 | size(img_slice,2) == 1 - set(side_ax,'visible','off'); - - if isfield(handles,'sagittal_slider') & ishandle(handles.sagittal_slider) - set(handles.sagittal_slider, 'visible', 'off'); - end + if isfield(handles, 'coronal_slider') & ishandle(handles.coronal_slider) + set(handles.coronal_slider, 'visible', 'off'); + end - if isfield(handles,'coronal_slider') & ishandle(handles.coronal_slider) - set(handles.coronal_slider, 'visible', 'off'); - end + if isfield(handles, 'axial_slider') & ishandle(handles.axial_slider) + set(handles.axial_slider, 'visible', 'off'); + end + end + + % plot sagittal view + % + if ~isempty(nii_view.bgimg) + bg_slice = squeeze(bgimg(nii_view.slices.sag, :, :)); + + h1 = plot_view(fig, ydim, zdim, side_ax, bg_slice', clim, cbarminmax, ... + handles, useimagesc, colorindex, color_map, ... + colorlevel, highcolor, useinterp, nii_view.numscan); + handles.sagittal_bg = h1; + else + handles.sagittal_bg = []; + end + + if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 + img_slice = squeeze(nii.img(nii_view.slices.sag, :, :, :, setscanid)); + img_slice = permute(img_slice, [2 1 3]); + else + img_slice = squeeze(nii.img(nii_view.slices.sag, :, :, setscanid)); + img_slice = img_slice'; + end + + h1 = plot_view(fig, ydim, zdim, side_ax, img_slice, clim, cbarminmax, ... + handles, useimagesc, colorindex, color_map, ... + colorlevel, highcolor, useinterp, nii_view.numscan); + set(h1, 'buttondown', 'view_nii(''sagittal_image'');'); + set(side_ax, 'Xdir', 'reverse'); + handles.sagittal_image = h1; + handles.sagittal_axes = side_ax; + + if size(img_slice, 1) == 1 | size(img_slice, 2) == 1 + set(side_ax, 'visible', 'off'); + + if isfield(handles, 'sagittal_slider') & ishandle(handles.sagittal_slider) + set(handles.sagittal_slider, 'visible', 'off'); + end - if isfield(handles,'axial_slider') & ishandle(handles.axial_slider) - set(handles.axial_slider, 'visible', 'off'); - end - end + if isfield(handles, 'coronal_slider') & ishandle(handles.coronal_slider) + set(handles.coronal_slider, 'visible', 'off'); + end - [top1_label, top2_label, side1_label, side2_label] = ... - dir_label(fig, top_ax, front_ax, side_ax); + if isfield(handles, 'axial_slider') & ishandle(handles.axial_slider) + set(handles.axial_slider, 'visible', 'off'); + end + end - % store label handles - % - handles.top1_label = top1_label; - handles.top2_label = top2_label; - handles.side1_label = side1_label; - handles.side2_label = side2_label; + [top1_label, top2_label, side1_label, side2_label] = ... + dir_label(fig, top_ax, front_ax, side_ax); - % plot colorbar - % - if ~isempty(cbar_axes) & ~isempty(cbarminmax_axes) + % store label handles + % + handles.top1_label = top1_label; + handles.top2_label = top2_label; + handles.side1_label = side1_label; + handles.side2_label = side2_label; -if 0 - if isempty(color_map) - level = colorlevel + num_highcolor; - else - level = size([color_map; highcolor], 1); - end -end + % plot colorbar + % + if ~isempty(cbar_axes) & ~isempty(cbarminmax_axes) + if 0 if isempty(color_map) - level = colorlevel; + level = colorlevel + num_highcolor; else - level = size([color_map], 1); + level = size([color_map; highcolor], 1); end + end - niiclass = class(nii.img); + if isempty(color_map) + level = colorlevel; + else + level = size([color_map], 1); + end - h1 = plot_cbar(fig, cbar_axes, cbarminmax_axes, cbarminmax, ... - level, handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, niiclass, nii_view.numscan); - handles.cbar_image = h1; - handles.cbar_axes = cbar_axes; - handles.cbarminmax_axes = cbarminmax_axes; + niiclass = class(nii.img); - end + h1 = plot_cbar(fig, cbar_axes, cbarminmax_axes, cbarminmax, ... + level, handles, useimagesc, colorindex, color_map, ... + colorlevel, highcolor, niiclass, nii_view.numscan); + handles.cbar_image = h1; + handles.cbar_axes = cbar_axes; + handles.cbarminmax_axes = cbarminmax_axes; - nii_view.handles = handles; % store handles + end - nii_view.usepanel = usepanel; % whole panel at low right cornor - nii_view.usestretch = usestretch; % stretch display of voxel_size - nii_view.useinterp = useinterp; % use interpolation - nii_view.colorindex = colorindex; % store colorindex variable - nii_view.buttondown = buttondown; % command after button down click - nii_view.cbarminmax = cbarminmax; % store min max value for colorbar + nii_view.handles = handles; % store handles - set_coordinates(nii_view,useinterp); % coord unit + nii_view.usepanel = usepanel; % whole panel at low right cornor + nii_view.usestretch = usestretch; % stretch display of voxel_size + nii_view.useinterp = useinterp; % use interpolation + nii_view.colorindex = colorindex; % store colorindex variable + nii_view.buttondown = buttondown; % command after button down click + nii_view.cbarminmax = cbarminmax; % store min max value for colorbar - if ~isfield(nii_view, 'axi_xhair') | ... - ~isfield(nii_view, 'cor_xhair') | ... - ~isfield(nii_view, 'sag_xhair') + set_coordinates(nii_view, useinterp); % coord unit - nii_view.axi_xhair = []; % top cross hair - nii_view.cor_xhair = []; % front cross hair - nii_view.sag_xhair = []; % side cross hair + if ~isfield(nii_view, 'axi_xhair') | ... + ~isfield(nii_view, 'cor_xhair') | ... + ~isfield(nii_view, 'sag_xhair') - end + nii_view.axi_xhair = []; % top cross hair + nii_view.cor_xhair = []; % front cross hair + nii_view.sag_xhair = []; % side cross hair - if ~isempty(color_map) - nii_view.color_map = color_map; - end + end - if ~isempty(colorlevel) - nii_view.colorlevel = colorlevel; - end + if ~isempty(color_map) + nii_view.color_map = color_map; + end - if ~isempty(highcolor) - nii_view.highcolor = highcolor; - end + if ~isempty(colorlevel) + nii_view.colorlevel = colorlevel; + end - update_nii_view(nii_view); + if ~isempty(highcolor) + nii_view.highcolor = highcolor; + end - if ~isempty(setunit) - update_unit(fig, setunit); - end + update_nii_view(nii_view); - if ~isempty(setviewpoint) - update_viewpoint(fig, setviewpoint); - end + if ~isempty(setunit) + update_unit(fig, setunit); + end - if ~isempty(setcrosshaircolor) - update_crosshaircolor(fig, setcrosshaircolor); - end + if ~isempty(setviewpoint) + update_viewpoint(fig, setviewpoint); + end - if ~isempty(usecrosshair) - update_usecrosshair(fig, usecrosshair); - end + if ~isempty(setcrosshaircolor) + update_crosshaircolor(fig, setcrosshaircolor); + end - nii_menu = getappdata(fig, 'nii_menu'); + if ~isempty(usecrosshair) + update_usecrosshair(fig, usecrosshair); + end - if ~isempty(nii_menu) - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(nii_menu.Minterp,'Userdata',1,'Label','Interp on','enable','off'); - elseif useinterp - set(nii_menu.Minterp,'Userdata',0,'Label','Interp off'); - else - set(nii_menu.Minterp,'Userdata',1,'Label','Interp on'); - end - end + nii_menu = getappdata(fig, 'nii_menu'); - windowbuttonmotion = get(fig, 'windowbuttonmotion'); - windowbuttonmotion = [windowbuttonmotion '; view_nii(''move_cursor'');']; - set(fig, 'windowbuttonmotion', windowbuttonmotion); + if ~isempty(nii_menu) + if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 + set(nii_menu.Minterp, 'Userdata', 1, 'Label', 'Interp on', 'enable', 'off'); + elseif useinterp + set(nii_menu.Minterp, 'Userdata', 0, 'Label', 'Interp off'); + else + set(nii_menu.Minterp, 'Userdata', 1, 'Label', 'Interp on'); + end + end - return; % init + windowbuttonmotion = get(fig, 'windowbuttonmotion'); + windowbuttonmotion = [windowbuttonmotion '; view_nii(''move_cursor'');']; + set(fig, 'windowbuttonmotion', windowbuttonmotion); + return % init -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function fig = update_img(img, fig, opt) - nii_menu = getappdata(fig,'nii_menu'); - - if ~isempty(nii_menu) - set(nii_menu.Mzoom,'Userdata',1,'Label','Zoom on'); - set(fig,'pointer','arrow'); - zoom off; - end + nii_menu = getappdata(fig, 'nii_menu'); - nii_view = getappdata(fig,'nii_view'); - change_interp = 0; + if ~isempty(nii_menu) + set(nii_menu.Mzoom, 'Userdata', 1, 'Label', 'Zoom on'); + set(fig, 'pointer', 'arrow'); + zoom off; + end - if isfield(opt, 'useinterp') & opt.useinterp ~= nii_view.useinterp - nii_view.useinterp = opt.useinterp; - change_interp = 1; - end + nii_view = getappdata(fig, 'nii_view'); + change_interp = 0; - setscanid = 1; + if isfield(opt, 'useinterp') & opt.useinterp ~= nii_view.useinterp + nii_view.useinterp = opt.useinterp; + change_interp = 1; + end - if isfield(opt, 'setscanid') - setscanid = round(opt.setscanid); + setscanid = 1; - if setscanid < 1 - setscanid = 1; - end + if isfield(opt, 'setscanid') + setscanid = round(opt.setscanid); - if setscanid > nii_view.numscan - setscanid = nii_view.numscan; - end - end + if setscanid < 1 + setscanid = 1; + end - if isfield(opt, 'glblocminmax') & ~isempty(opt.glblocminmax) + if setscanid > nii_view.numscan + setscanid = nii_view.numscan; + end + end + + if isfield(opt, 'glblocminmax') & ~isempty(opt.glblocminmax) + minvalue = opt.glblocminmax(1); + maxvalue = opt.glblocminmax(2); + else + minvalue = img(:, :, :, setscanid); + minvalue = double(minvalue(:)); + minvalue = min(minvalue(~isnan(minvalue))); + maxvalue = img(:, :, :, setscanid); + maxvalue = double(maxvalue(:)); + maxvalue = max(maxvalue(~isnan(maxvalue))); + end + + if isfield(opt, 'setvalue') + setvalue = opt.setvalue; + + if isfield(opt, 'glblocminmax') & ~isempty(opt.glblocminmax) minvalue = opt.glblocminmax(1); maxvalue = opt.glblocminmax(2); - else - minvalue = img(:,:,:,setscanid); - minvalue = double(minvalue(:)); - minvalue = min(minvalue(~isnan(minvalue))); - maxvalue = img(:,:,:,setscanid); - maxvalue = double(maxvalue(:)); - maxvalue = max(maxvalue(~isnan(maxvalue))); - end - - if isfield(opt, 'setvalue') - setvalue = opt.setvalue; - - if isfield(opt, 'glblocminmax') & ~isempty(opt.glblocminmax) - minvalue = opt.glblocminmax(1); - maxvalue = opt.glblocminmax(2); - else - minvalue = double(min(setvalue.val)); - maxvalue = double(max(setvalue.val)); - end - - bgimg = double(img); - minbg = double(min(bgimg(:))); - maxbg = double(max(bgimg(:))); + else + minvalue = double(min(setvalue.val)); + maxvalue = double(max(setvalue.val)); + end - bgimg = scale_in(bgimg, minbg, maxbg, 55) + 200; % scale to 201~256 + bgimg = double(img); + minbg = double(min(bgimg(:))); + maxbg = double(max(bgimg(:))); - cbarminmax = [minvalue maxvalue]; + bgimg = scale_in(bgimg, minbg, maxbg, 55) + 200; % scale to 201~256 - if nii_view.useinterp + cbarminmax = [minvalue maxvalue]; - % scale signal data to 1~200 - % - img = repmat(nan, size(img)); - img(setvalue.idx) = setvalue.val; + if nii_view.useinterp - % 200 level for source image - % - bgimg = single(scale_out(bgimg, cbarminmax(1), cbarminmax(2), 199)); + % scale signal data to 1~200 + % + img = repmat(nan, size(img)); + img(setvalue.idx) = setvalue.val; - else + % 200 level for source image + % + bgimg = single(scale_out(bgimg, cbarminmax(1), cbarminmax(2), 199)); - bgimg(setvalue.idx) = NaN; - minbg = double(min(bgimg(:))); - maxbg = double(max(bgimg(:))); - bgimg(setvalue.idx) = minbg; + else - % bgimg must be normalized to [201 256] - % - bgimg = 55 * (bgimg-min(bgimg(:))) / (max(bgimg(:))-min(bgimg(:))) + 201; - bgimg(setvalue.idx) = 0; + bgimg(setvalue.idx) = NaN; + minbg = double(min(bgimg(:))); + maxbg = double(max(bgimg(:))); + bgimg(setvalue.idx) = minbg; - % scale signal data to 1~200 - % - img = zeros(size(img)); - img(setvalue.idx) = scale_in(setvalue.val, minvalue, maxvalue, 199); - img = img + bgimg; - bgimg = []; - img = scale_out(img, cbarminmax(1), cbarminmax(2), 199); + % bgimg must be normalized to [201 256] + % + bgimg = 55 * (bgimg - min(bgimg(:))) / (max(bgimg(:)) - min(bgimg(:))) + 201; + bgimg(setvalue.idx) = 0; - minvalue = double(min(img(:))); - maxvalue = double(max(img(:))); + % scale signal data to 1~200 + % + img = zeros(size(img)); + img(setvalue.idx) = scale_in(setvalue.val, minvalue, maxvalue, 199); + img = img + bgimg; + bgimg = []; + img = scale_out(img, cbarminmax(1), cbarminmax(2), 199); - if isfield(opt,'glblocminmax') & ~isempty(opt.glblocminmax) - minvalue = opt.glblocminmax(1); - end + minvalue = double(min(img(:))); + maxvalue = double(max(img(:))); + if isfield(opt, 'glblocminmax') & ~isempty(opt.glblocminmax) + minvalue = opt.glblocminmax(1); end - nii_view.bgimg = bgimg; - nii_view.setvalue = setvalue; - - else - cbarminmax = [minvalue maxvalue]; - end - - update_cbarminmax(fig, cbarminmax); - nii_view.cbarminmax = cbarminmax; - nii_view.nii.img = img; - nii_view.minvalue = minvalue; - nii_view.maxvalue = maxvalue; - nii_view.scanid = setscanid; - change_colormap(fig); - - % init color (gray) scaling to make sure the slice clim take the - % global clim [min(nii.img(:)) max(nii.img(:))] - % - if isempty(nii_view.bgimg) - clim = [minvalue maxvalue]; - else - clim = [minvalue double(max(nii_view.bgimg(:)))]; - end - - if clim(1) == clim(2) - clim(2) = clim(1) + 0.000001; - end - - if strcmpi(get(nii_view.handles.axial_image,'cdatamapping'), 'direct') - useimagesc = 0; - else - useimagesc = 1; - end - - if ~isempty(nii_view.bgimg) % with interpolation + end - Saxi = squeeze(nii_view.bgimg(:,:,nii_view.slices.axi)); + nii_view.bgimg = bgimg; + nii_view.setvalue = setvalue; + + else + cbarminmax = [minvalue maxvalue]; + end + + update_cbarminmax(fig, cbarminmax); + nii_view.cbarminmax = cbarminmax; + nii_view.nii.img = img; + nii_view.minvalue = minvalue; + nii_view.maxvalue = maxvalue; + nii_view.scanid = setscanid; + change_colormap(fig); + + % init color (gray) scaling to make sure the slice clim take the + % global clim [min(nii.img(:)) max(nii.img(:))] + % + if isempty(nii_view.bgimg) + clim = [minvalue maxvalue]; + else + clim = [minvalue double(max(nii_view.bgimg(:)))]; + end + + if clim(1) == clim(2) + clim(2) = clim(1) + 0.000001; + end + + if strcmpi(get(nii_view.handles.axial_image, 'cdatamapping'), 'direct') + useimagesc = 0; + else + useimagesc = 1; + end + + if ~isempty(nii_view.bgimg) % with interpolation + + Saxi = squeeze(nii_view.bgimg(:, :, nii_view.slices.axi)); + + if isfield(nii_view.handles, 'axial_bg') & ~isempty(nii_view.handles.axial_bg) + set(nii_view.handles.axial_bg, 'CData', double(Saxi)'); + else + axes(nii_view.handles.axial_axes); - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) - set(nii_view.handles.axial_bg,'CData',double(Saxi)'); + if useimagesc + nii_view.handles.axial_bg = surface(zeros(size(Saxi')), double(Saxi'), 'edgecolor', 'none', 'facecolor', 'interp'); else - axes(nii_view.handles.axial_axes); - - if useimagesc - nii_view.handles.axial_bg = surface(zeros(size(Saxi')),double(Saxi'),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.axial_bg = surface(zeros(size(Saxi')),double(Saxi'),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end - - order = get(gca,'child'); - order(find(order == nii_view.handles.axial_bg)) = []; - order = [order; nii_view.handles.axial_bg]; - set(gca, 'child', order); + nii_view.handles.axial_bg = surface(zeros(size(Saxi')), double(Saxi'), 'cdatamapping', 'direct', 'edgecolor', 'none', 'facecolor', 'interp'); end - end - - if isfield(nii_view.handles,'axial_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Saxi = squeeze(nii_view.nii.img(:,:,nii_view.slices.axi,:,setscanid)); - Saxi = permute(Saxi, [2 1 3]); - else - Saxi = squeeze(nii_view.nii.img(:,:,nii_view.slices.axi,setscanid)); - Saxi = Saxi'; - end - - set(nii_view.handles.axial_image,'CData',double(Saxi)); - end - - set(nii_view.handles.axial_axes,'CLim',clim); - - if ~isempty(nii_view.bgimg) - Scor = squeeze(nii_view.bgimg(:,nii_view.slices.cor,:)); + order = get(gca, 'child'); + order(find(order == nii_view.handles.axial_bg)) = []; + order = [order; nii_view.handles.axial_bg]; + set(gca, 'child', order); + end - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) - set(nii_view.handles.coronal_bg,'CData',double(Scor)'); - else - axes(nii_view.handles.coronal_axes); - - if useimagesc - nii_view.handles.coronal_bg = surface(zeros(size(Scor')),double(Scor'),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.coronal_bg = surface(zeros(size(Scor')),double(Scor'),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end - - order = get(gca,'child'); - order(find(order == nii_view.handles.coronal_bg)) = []; - order = [order; nii_view.handles.coronal_bg]; - set(gca, 'child', order); - end - end + end - if isfield(nii_view.handles,'coronal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Scor = squeeze(nii_view.nii.img(:,nii_view.slices.cor,:,:,setscanid)); - Scor = permute(Scor, [2 1 3]); - else - Scor = squeeze(nii_view.nii.img(:,nii_view.slices.cor,:,setscanid)); - Scor = Scor'; - end + if isfield(nii_view.handles, 'axial_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Saxi = squeeze(nii_view.nii.img(:, :, nii_view.slices.axi, :, setscanid)); + Saxi = permute(Saxi, [2 1 3]); + else + Saxi = squeeze(nii_view.nii.img(:, :, nii_view.slices.axi, setscanid)); + Saxi = Saxi'; + end - set(nii_view.handles.coronal_image,'CData',double(Scor)); - end + set(nii_view.handles.axial_image, 'CData', double(Saxi)); + end - set(nii_view.handles.coronal_axes,'CLim',clim); + set(nii_view.handles.axial_axes, 'CLim', clim); - if ~isempty(nii_view.bgimg) - Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag,:,:)); + if ~isempty(nii_view.bgimg) + Scor = squeeze(nii_view.bgimg(:, nii_view.slices.cor, :)); - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) - set(nii_view.handles.sagittal_bg,'CData',double(Ssag)'); - else - axes(nii_view.handles.sagittal_axes); - - if useimagesc - nii_view.handles.sagittal_bg = surface(zeros(size(Ssag')),double(Ssag'),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.sagittal_bg = surface(zeros(size(Ssag')),double(Ssag'),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end - - order = get(gca,'child'); - order(find(order == nii_view.handles.sagittal_bg)) = []; - order = [order; nii_view.handles.sagittal_bg]; - set(gca, 'child', order); - end - end + if isfield(nii_view.handles, 'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) + set(nii_view.handles.coronal_bg, 'CData', double(Scor)'); + else + axes(nii_view.handles.coronal_axes); - if isfield(nii_view.handles,'sagittal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Ssag = squeeze(nii_view.nii.img(nii_view.slices.sag,:,:,:,setscanid)); - Ssag = permute(Ssag, [2 1 3]); + if useimagesc + nii_view.handles.coronal_bg = surface(zeros(size(Scor')), double(Scor'), 'edgecolor', 'none', 'facecolor', 'interp'); else - Ssag = squeeze(nii_view.nii.img(nii_view.slices.sag,:,:,setscanid)); - Ssag = Ssag'; - end - - set(nii_view.handles.sagittal_image,'CData',double(Ssag)); - end - - set(nii_view.handles.sagittal_axes,'CLim',clim); - - update_nii_view(nii_view); - - if isfield(opt, 'setvalue') - - if ~isfield(nii_view,'highcolor') | ~isequal(size(nii_view.highcolor),[56 3]) - - % 55 level for brain structure (paded 0 for highcolor level 1, i.e. normal level 201, to make 56 highcolor) - % - update_highcolor(fig, [zeros(1,3);gray(55)], []); - - end - - if nii_view.colorindex ~= 2 - update_colorindex(fig, 2); - end - - old_color = get(nii_view.handles.xhair_color,'user'); - - if isequal(old_color, [1 0 0]) - update_crosshaircolor(fig, [1 1 0]); + nii_view.handles.coronal_bg = surface(zeros(size(Scor')), double(Scor'), 'cdatamapping', 'direct', 'edgecolor', 'none', 'facecolor', 'interp'); end -% if change_interp - % update_useinterp(fig, nii_view.useinterp); - % end - - end - - if change_interp - update_useinterp(fig, nii_view.useinterp); - end - - return; % update_img - - -%---------------------------------------------------------------- -function [top_pos, front_pos, side_pos] = ... - axes_pos(fig,area,vol_size,usestretch) + order = get(gca, 'child'); + order(find(order == nii_view.handles.coronal_bg)) = []; + order = [order; nii_view.handles.coronal_bg]; + set(gca, 'child', order); + end + end - set(fig,'unit','pixel'); + if isfield(nii_view.handles, 'coronal_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Scor = squeeze(nii_view.nii.img(:, nii_view.slices.cor, :, :, setscanid)); + Scor = permute(Scor, [2 1 3]); + else + Scor = squeeze(nii_view.nii.img(:, nii_view.slices.cor, :, setscanid)); + Scor = Scor'; + end - fig_pos = get(fig,'position'); + set(nii_view.handles.coronal_image, 'CData', double(Scor)); + end - gap_x = 15/fig_pos(3); % width of vertical scrollbar - gap_y = 15/fig_pos(4); % width of horizontal scrollbar + set(nii_view.handles.coronal_axes, 'CLim', clim); - a = (area(3) - gap_x * 1.3) * fig_pos(3) / (vol_size(1) + vol_size(2)); % no crosshair lost in zoom - b = (area(4) - gap_y * 3) * fig_pos(4) / (vol_size(2) + vol_size(3)); - c = min([a b]); % make sure 'ax' is inside 'area' + if ~isempty(nii_view.bgimg) + Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag, :, :)); - top_w = vol_size(1) * c / fig_pos(3); - side_w = vol_size(2) * c / fig_pos(3); - top_h = vol_size(2) * c / fig_pos(4); - side_h = vol_size(3) * c / fig_pos(4); - side_x = area(1) + top_w + gap_x * 1.3; % no crosshair lost in zoom - side_y = area(2) + top_h + gap_y * 3; + if isfield(nii_view.handles, 'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) + set(nii_view.handles.sagittal_bg, 'CData', double(Ssag)'); + else + axes(nii_view.handles.sagittal_axes); - if usestretch - if a > b % top touched ceiling, use b - d = (area(3) - gap_x * 1.3) / (top_w + side_w); % no crosshair lost in zoom - top_w = top_w * d; - side_w = side_w * d; - side_x = area(1) + top_w + gap_x * 1.3; % no crosshair lost in zoom + if useimagesc + nii_view.handles.sagittal_bg = surface(zeros(size(Ssag')), double(Ssag'), 'edgecolor', 'none', 'facecolor', 'interp'); else - d = (area(4) - gap_y * 3) / (top_h + side_h); - top_h = top_h * d; - side_h = side_h * d; - side_y = area(2) + top_h + gap_y * 3; + nii_view.handles.sagittal_bg = surface(zeros(size(Ssag')), double(Ssag'), 'cdatamapping', 'direct', 'edgecolor', 'none', 'facecolor', 'interp'); end - end - - top_pos = [area(1) area(2)+gap_y top_w top_h]; - front_pos = [area(1) side_y top_w side_h]; - side_pos = [side_x side_y side_w side_h]; - set(fig,'unit','normal'); + order = get(gca, 'child'); + order(find(order == nii_view.handles.sagittal_bg)) = []; + order = [order; nii_view.handles.sagittal_bg]; + set(gca, 'child', order); + end + end - return; % axes_pos + if isfield(nii_view.handles, 'sagittal_image') + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + Ssag = squeeze(nii_view.nii.img(nii_view.slices.sag, :, :, :, setscanid)); + Ssag = permute(Ssag, [2 1 3]); + else + Ssag = squeeze(nii_view.nii.img(nii_view.slices.sag, :, :, setscanid)); + Ssag = Ssag'; + end + set(nii_view.handles.sagittal_image, 'CData', double(Ssag)); + end -%---------------------------------------------------------------- -function [top_ax, front_ax, side_ax] ... - = create_ax(fig, area, vol_size, usestretch) + set(nii_view.handles.sagittal_axes, 'CLim', clim); - cur_fig = gcf; % save h_wait fig - figure(fig); + update_nii_view(nii_view); - [top_pos, front_pos, side_pos] = ... - axes_pos(fig,area,vol_size,usestretch); + if isfield(opt, 'setvalue') - nii_view = getappdata(fig, 'nii_view'); + if ~isfield(nii_view, 'highcolor') | ~isequal(size(nii_view.highcolor), [56 3]) - if isempty(nii_view) - top_ax = axes('position', top_pos); - front_ax = axes('position', front_pos); - side_ax = axes('position', side_pos); - else - top_ax = nii_view.handles.axial_axes; - front_ax = nii_view.handles.coronal_axes; - side_ax = nii_view.handles.sagittal_axes; + % 55 level for brain structure (paded 0 for highcolor level 1, i.e. normal level 201, to make 56 highcolor) + % + update_highcolor(fig, [zeros(1, 3); gray(55)], []); - set(top_ax, 'position', top_pos); - set(front_ax, 'position', front_pos); - set(side_ax, 'position', side_pos); - end + end - figure(cur_fig); + if nii_view.colorindex ~= 2 + update_colorindex(fig, 2); + end - return; % create_ax + old_color = get(nii_view.handles.xhair_color, 'user'); + if isequal(old_color, [1 0 0]) + update_crosshaircolor(fig, [1 1 0]); + end -%---------------------------------------------------------------- -function [cbar_axes, cbarminmax_axes] = create_cbar_axes(fig, cbar_area, nii_view) + % if change_interp + % update_useinterp(fig, nii_view.useinterp); + % end - if isempty(cbar_area) % without_cbar - cbar_axes = []; - cbarminmax_axes = []; - return; - end + end - cur_fig = gcf; % save h_wait fig - figure(fig); + if change_interp + update_useinterp(fig, nii_view.useinterp); + end - if ~exist('nii_view', 'var') - nii_view = getappdata(fig, 'nii_view'); - end + return % update_img - if isempty(nii_view) | ~isfield(nii_view.handles,'cbar_axes') | isempty(nii_view.handles.cbar_axes) - cbarminmax_axes = axes('position', cbar_area); - cbar_axes = axes('position', cbar_area); - else - cbarminmax_axes = nii_view.handles.cbarminmax_axes; - cbar_axes = nii_view.handles.cbar_axes; - set(cbarminmax_axes, 'position', cbar_area); - set(cbar_axes, 'position', cbar_area); - end + % ---------------------------------------------------------------- +function [top_pos, front_pos, side_pos] = ... + axes_pos(fig, area, vol_size, usestretch) - figure(cur_fig); + set(fig, 'unit', 'pixel'); - return; % create_cbar_axes + fig_pos = get(fig, 'position'); + gap_x = 15 / fig_pos(3); % width of vertical scrollbar + gap_y = 15 / fig_pos(4); % width of horizontal scrollbar -%---------------------------------------------------------------- -function h1 = plot_view(fig, x, y, img_ax, img_slice, clim, ... - cbarminmax, handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, numscan) + a = (area(3) - gap_x * 1.3) * fig_pos(3) / (vol_size(1) + vol_size(2)); % no crosshair lost in zoom + b = (area(4) - gap_y * 3) * fig_pos(4) / (vol_size(2) + vol_size(3)); + c = min([a b]); % make sure 'ax' is inside 'area' - h1 = []; + top_w = vol_size(1) * c / fig_pos(3); + side_w = vol_size(2) * c / fig_pos(3); + top_h = vol_size(2) * c / fig_pos(4); + side_h = vol_size(3) * c / fig_pos(4); + side_x = area(1) + top_w + gap_x * 1.3; % no crosshair lost in zoom + side_y = area(2) + top_h + gap_y * 3; - if x > 1 & y > 1, + if usestretch + if a > b % top touched ceiling, use b + d = (area(3) - gap_x * 1.3) / (top_w + side_w); % no crosshair lost in zoom + top_w = top_w * d; + side_w = side_w * d; + side_x = area(1) + top_w + gap_x * 1.3; % no crosshair lost in zoom + else + d = (area(4) - gap_y * 3) / (top_h + side_h); + top_h = top_h * d; + side_h = side_h * d; + side_y = area(2) + top_h + gap_y * 3; + end + end - axes(img_ax); + top_pos = [area(1) area(2) + gap_y top_w top_h]; + front_pos = [area(1) side_y top_w side_h]; + side_pos = [side_x side_y side_w side_h]; - nii_view = getappdata(fig, 'nii_view'); + set(fig, 'unit', 'normal'); - if isempty(nii_view) + return % axes_pos - % set colormap first - % - nii.handles = handles; - nii.handles.axial_axes = img_ax; - nii.colorindex = colorindex; - nii.color_map = color_map; - nii.colorlevel = colorlevel; - nii.highcolor = highcolor; - nii.numscan = numscan; - - change_colormap(fig, nii, colorindex, cbarminmax); - - if useinterp - if useimagesc - h1 = surface(zeros(size(img_slice)),double(img_slice),'edgecolor','none','facecolor','interp'); - else - h1 = surface(zeros(size(img_slice)),double(img_slice),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end - - set(gca,'clim',clim); - else - if useimagesc - h1 = imagesc(img_slice,clim); - else - h1 = image(img_slice); - end - - set(gca,'clim',clim); - end + % ---------------------------------------------------------------- +function [top_ax, front_ax, side_ax] ... + = create_ax(fig, area, vol_size, usestretch) - else + cur_fig = gcf; % save h_wait fig + figure(fig); - h1 = nii_view.handles.axial_image; + [top_pos, front_pos, side_pos] = ... + axes_pos(fig, area, vol_size, usestretch); - if ~isequal(get(h1,'parent'), img_ax) - h1 = nii_view.handles.coronal_image; - end + nii_view = getappdata(fig, 'nii_view'); - if ~isequal(get(h1,'parent'), img_ax) - h1 = nii_view.handles.sagittal_image; - end + if isempty(nii_view) + top_ax = axes('position', top_pos); + front_ax = axes('position', front_pos); + side_ax = axes('position', side_pos); + else + top_ax = nii_view.handles.axial_axes; + front_ax = nii_view.handles.coronal_axes; + side_ax = nii_view.handles.sagittal_axes; - set(h1, 'cdata', double(img_slice)); - set(h1, 'xdata', 1:size(img_slice,2)); - set(h1, 'ydata', 1:size(img_slice,1)); + set(top_ax, 'position', top_pos); + set(front_ax, 'position', front_pos); + set(side_ax, 'position', side_pos); + end - end + figure(cur_fig); - set(img_ax,'YDir','normal','XLimMode','manual','YLimMode','manual',... - 'ClimMode','manual','visible','off', ... - 'xtick',[],'ytick',[], 'clim', clim); + return % create_ax - end + % ---------------------------------------------------------------- +function [cbar_axes, cbarminmax_axes] = create_cbar_axes(fig, cbar_area, nii_view) - return; % plot_view + if isempty(cbar_area) % without_cbar + cbar_axes = []; + cbarminmax_axes = []; + return + end + cur_fig = gcf; % save h_wait fig + figure(fig); -%---------------------------------------------------------------- -function h1 = plot_cbar(fig, cbar_axes, cbarminmax_axes, cbarminmax, ... - level, handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, niiclass, numscan, nii_view) + if ~exist('nii_view', 'var') + nii_view = getappdata(fig, 'nii_view'); + end - cbar_image = [1:level]'; + if isempty(nii_view) | ~isfield(nii_view.handles, 'cbar_axes') | isempty(nii_view.handles.cbar_axes) + cbarminmax_axes = axes('position', cbar_area); + cbar_axes = axes('position', cbar_area); + else + cbarminmax_axes = nii_view.handles.cbarminmax_axes; + cbar_axes = nii_view.handles.cbar_axes; + set(cbarminmax_axes, 'position', cbar_area); + set(cbar_axes, 'position', cbar_area); + end - % In a uint8 or uint16 indexed image, 0 points to the first row - % in the colormap - % - if 0 % strcmpi(niiclass,'uint8') | strcmpi(niiclass,'uint16') - % we use single for display anyway - ylim = [0, level-1]; - else - ylim = [1, level]; - end + figure(cur_fig); - axes(cbarminmax_axes); + return % create_cbar_axes - plot([0 0], cbarminmax, 'w'); - axis tight; + % ---------------------------------------------------------------- +function h1 = plot_view(fig, x, y, img_ax, img_slice, clim, ... + cbarminmax, handles, useimagesc, colorindex, color_map, ... + colorlevel, highcolor, useinterp, numscan) - set(cbarminmax_axes,'YDir','normal', ... - 'XLimMode','manual','YLimMode','manual','YColor',[0 0 0], ... - 'XColor',[0 0 0],'xtick',[],'YAxisLocation','right'); + h1 = []; - ylimb = get(cbarminmax_axes,'ylim'); - ytickb = get(cbarminmax_axes,'ytick'); - ytick=(ylim(2)-ylim(1))*(ytickb-ylimb(1))/(ylimb(2)-ylimb(1))+ylim(1); + if x > 1 & y > 1 - axes(cbar_axes); + axes(img_ax); - if ~exist('nii_view', 'var') - nii_view = getappdata(fig, 'nii_view'); - end + nii_view = getappdata(fig, 'nii_view'); - if isempty(nii_view) | ~isfield(nii_view.handles,'cbar_image') | isempty(nii_view.handles.cbar_image) + if isempty(nii_view) % set colormap first % nii.handles = handles; + nii.handles.axial_axes = img_ax; nii.colorindex = colorindex; nii.color_map = color_map; nii.colorlevel = colorlevel; @@ -2855,2019 +2783,2094 @@ nii.numscan = numscan; change_colormap(fig, nii, colorindex, cbarminmax); - h1 = image([0,1], [ylim(1),ylim(2)], cbar_image); - - else - h1 = nii_view.handles.cbar_image; - set(h1, 'cdata', double(cbar_image)); - end - - set(cbar_axes,'YDir','normal','XLimMode','manual', ... - 'YLimMode','manual','YColor',[0 0 0],'XColor',[0 0 0],'xtick',[], ... - 'YAxisLocation','right','ylim',ylim,'ytick',ytick,'yticklabel',''); - - return; % plot_cbar + if useinterp + if useimagesc + h1 = surface(zeros(size(img_slice)), double(img_slice), 'edgecolor', 'none', 'facecolor', 'interp'); + else + h1 = surface(zeros(size(img_slice)), double(img_slice), 'cdatamapping', 'direct', 'edgecolor', 'none', 'facecolor', 'interp'); + end -%---------------------------------------------------------------- -function set_coordinates(nii_view,useinterp) + set(gca, 'clim', clim); + else + if useimagesc + h1 = imagesc(img_slice, clim); + else + h1 = image(img_slice); + end - imgPlim.vox = nii_view.dims; - imgNlim.vox = [1 1 1]; + set(gca, 'clim', clim); + end - if useinterp - xdata_ax = [imgNlim.vox(1) imgPlim.vox(1)]; - ydata_ax = [imgNlim.vox(2) imgPlim.vox(2)]; - zdata_ax = [imgNlim.vox(3) imgPlim.vox(3)]; else - xdata_ax = [imgNlim.vox(1)-0.5 imgPlim.vox(1)+0.5]; - ydata_ax = [imgNlim.vox(2)-0.5 imgPlim.vox(2)+0.5]; - zdata_ax = [imgNlim.vox(3)-0.5 imgPlim.vox(3)+0.5]; - end - - if isfield(nii_view.handles,'axial_image') & ~isempty(nii_view.handles.axial_image) - set(nii_view.handles.axial_axes,'Xlim',xdata_ax); - set(nii_view.handles.axial_axes,'Ylim',ydata_ax); - end; - if isfield(nii_view.handles,'coronal_image') & ~isempty(nii_view.handles.coronal_image) - set(nii_view.handles.coronal_axes,'Xlim',xdata_ax); - set(nii_view.handles.coronal_axes,'Ylim',zdata_ax); - end; - if isfield(nii_view.handles,'sagittal_image') & ~isempty(nii_view.handles.sagittal_image) - set(nii_view.handles.sagittal_axes,'Xlim',ydata_ax); - set(nii_view.handles.sagittal_axes,'Ylim',zdata_ax); - end; - return % set_coordinates + h1 = nii_view.handles.axial_image; + if ~isequal(get(h1, 'parent'), img_ax) + h1 = nii_view.handles.coronal_image; + end -%---------------------------------------------------------------- -function set_image_value(nii_view), + if ~isequal(get(h1, 'parent'), img_ax) + h1 = nii_view.handles.sagittal_image; + end - % get coordinates of selected voxel and the image intensity there - % - sag = round(nii_view.slices.sag); - cor = round(nii_view.slices.cor); - axi = round(nii_view.slices.axi); + set(h1, 'cdata', double(img_slice)); + set(h1, 'xdata', 1:size(img_slice, 2)); + set(h1, 'ydata', 1:size(img_slice, 1)); - if 0 % isfield(nii_view, 'disp') - img = nii_view.disp; - else - img = nii_view.nii.img; end - if nii_view.nii.hdr.dime.datatype == 128 - imgvalue = [double(img(sag,cor,axi,1,nii_view.scanid)) double(img(sag,cor,axi,2,nii_view.scanid)) double(img(sag,cor,axi,3,nii_view.scanid))]; - set(nii_view.handles.imval,'Value',imgvalue); - set(nii_view.handles.imval,'String',sprintf('%7.4g %7.4g %7.4g',imgvalue)); - elseif nii_view.nii.hdr.dime.datatype == 511 - R = double(img(sag,cor,axi,1,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - G = double(img(sag,cor,axi,2,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - B = double(img(sag,cor,axi,3,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - imgvalue = [double(img(sag,cor,axi,1,nii_view.scanid)) double(img(sag,cor,axi,2,nii_view.scanid)) double(img(sag,cor,axi,3,nii_view.scanid))]; - set(nii_view.handles.imval,'Value',imgvalue); - imgvalue = [R G B]; - set(nii_view.handles.imval,'String',sprintf('%7.4g %7.4g %7.4g',imgvalue)); - else - imgvalue = double(img(sag,cor,axi,nii_view.scanid)); - set(nii_view.handles.imval,'Value',imgvalue); + set(img_ax, 'YDir', 'normal', 'XLimMode', 'manual', 'YLimMode', 'manual', ... + 'ClimMode', 'manual', 'visible', 'off', ... + 'xtick', [], 'ytick', [], 'clim', clim); - if isnan(imgvalue) | imgvalue > nii_view.cbarminmax(2) - imgvalue = 0; - end + end - set(nii_view.handles.imval,'String',sprintf('%.6g',imgvalue)); - end + return % plot_view - % Now update the coordinates of the selected voxel - - nii_view = update_imgXYZ(nii_view); - - if get(nii_view.handles.coord,'value') == 1, - sag = nii_view.imgXYZ.vox(1); - cor = nii_view.imgXYZ.vox(2); - axi = nii_view.imgXYZ.vox(3); - org = nii_view.origin; - elseif get(nii_view.handles.coord,'value') == 2, - sag = nii_view.imgXYZ.mm(1); - cor = nii_view.imgXYZ.mm(2); - axi = nii_view.imgXYZ.mm(3); - org = [0 0 0]; - elseif get(nii_view.handles.coord,'value') == 3, - sag = nii_view.imgXYZ.tal(1); - cor = nii_view.imgXYZ.tal(2); - axi = nii_view.imgXYZ.tal(3); - org = [0 0 0]; - end + % ---------------------------------------------------------------- +function h1 = plot_cbar(fig, cbar_axes, cbarminmax_axes, cbarminmax, ... + level, handles, useimagesc, colorindex, color_map, ... + colorlevel, highcolor, niiclass, numscan, nii_view) - set(nii_view.handles.impos,'Value',[sag,cor,axi]); + cbar_image = [1:level]'; - if get(nii_view.handles.coord,'value') == 1, - string = sprintf('%7.0f %7.0f %7.0f',sag,cor,axi); - org_str = sprintf('%7.0f %7.0f %7.0f', org(1), org(2), org(3)); - else - string = sprintf('%7.1f %7.1f %7.1f',sag,cor,axi); - org_str = sprintf('%7.1f %7.1f %7.1f', org(1), org(2), org(3)); - end; - - set(nii_view.handles.impos,'String',string); - set(nii_view.handles.origin, 'string', org_str); + % In a uint8 or uint16 indexed image, 0 points to the first row + % in the colormap + % + if 0 % strcmpi(niiclass,'uint8') | strcmpi(niiclass,'uint16') + % we use single for display anyway + ylim = [0, level - 1]; + else + ylim = [1, level]; + end - return % set_image_value + axes(cbarminmax_axes); + plot([0 0], cbarminmax, 'w'); + axis tight; -%---------------------------------------------------------------- -function nii_view = get_slice_position(nii_view,view), + set(cbarminmax_axes, 'YDir', 'normal', ... + 'XLimMode', 'manual', 'YLimMode', 'manual', 'YColor', [0 0 0], ... + 'XColor', [0 0 0], 'xtick', [], 'YAxisLocation', 'right'); - % obtain slices that is in correct unit, then update slices - % - slices = nii_view.slices; - - switch view, - case 'sag', - currentpoint = get(nii_view.handles.sagittal_axes,'CurrentPoint'); - slices.cor = currentpoint(1,1); - slices.axi = currentpoint(1,2); - case 'cor', - currentpoint = get(nii_view.handles.coronal_axes,'CurrentPoint'); - slices.sag = currentpoint(1,1); - slices.axi = currentpoint(1,2); - case 'axi', - currentpoint = get(nii_view.handles.axial_axes,'CurrentPoint'); - slices.sag = currentpoint(1,1); - slices.cor = currentpoint(1,2); - end + ylimb = get(cbarminmax_axes, 'ylim'); + ytickb = get(cbarminmax_axes, 'ytick'); + ytick = (ylim(2) - ylim(1)) * (ytickb - ylimb(1)) / (ylimb(2) - ylimb(1)) + ylim(1); - % update nii_view.slices with the updated slices - % - nii_view.slices.axi = round(slices.axi); - nii_view.slices.cor = round(slices.cor); - nii_view.slices.sag = round(slices.sag); + axes(cbar_axes); - return % get_slice_position + if ~exist('nii_view', 'var') + nii_view = getappdata(fig, 'nii_view'); + end + if isempty(nii_view) | ~isfield(nii_view.handles, 'cbar_image') | isempty(nii_view.handles.cbar_image) -%---------------------------------------------------------------- -function nii_view = get_slider_position(nii_view), - - [nii_view.slices.sag,nii_view.slices.cor,nii_view.slices.axi] = deal(0); - - if isfield(nii_view.handles,'sagittal_slider'), - if ishandle(nii_view.handles.sagittal_slider), - nii_view.slices.sag = ... - round(get(nii_view.handles.sagittal_slider,'Value')); - end - end - - if isfield(nii_view.handles,'coronal_slider'), - if ishandle(nii_view.handles.coronal_slider), - nii_view.slices.cor = ... - round(nii_view.dims(2) - ... - get(nii_view.handles.coronal_slider,'Value') + 1); - end - end - - if isfield(nii_view.handles,'axial_slider'), - if ishandle(nii_view.handles.axial_slider), - nii_view.slices.axi = ... - round(get(nii_view.handles.axial_slider,'Value')); - end + % set colormap first + % + nii.handles = handles; + nii.colorindex = colorindex; + nii.color_map = color_map; + nii.colorlevel = colorlevel; + nii.highcolor = highcolor; + nii.numscan = numscan; + + change_colormap(fig, nii, colorindex, cbarminmax); + h1 = image([0, 1], [ylim(1), ylim(2)], cbar_image); + + else + h1 = nii_view.handles.cbar_image; + set(h1, 'cdata', double(cbar_image)); + end + + set(cbar_axes, 'YDir', 'normal', 'XLimMode', 'manual', ... + 'YLimMode', 'manual', 'YColor', [0 0 0], 'XColor', [0 0 0], 'xtick', [], ... + 'YAxisLocation', 'right', 'ylim', ylim, 'ytick', ytick, 'yticklabel', ''); + + return % plot_cbar + + % ---------------------------------------------------------------- +function set_coordinates(nii_view, useinterp) + + imgPlim.vox = nii_view.dims; + imgNlim.vox = [1 1 1]; + + if useinterp + xdata_ax = [imgNlim.vox(1) imgPlim.vox(1)]; + ydata_ax = [imgNlim.vox(2) imgPlim.vox(2)]; + zdata_ax = [imgNlim.vox(3) imgPlim.vox(3)]; + else + xdata_ax = [imgNlim.vox(1) - 0.5 imgPlim.vox(1) + 0.5]; + ydata_ax = [imgNlim.vox(2) - 0.5 imgPlim.vox(2) + 0.5]; + zdata_ax = [imgNlim.vox(3) - 0.5 imgPlim.vox(3) + 0.5]; + end + + if isfield(nii_view.handles, 'axial_image') & ~isempty(nii_view.handles.axial_image) + set(nii_view.handles.axial_axes, 'Xlim', xdata_ax); + set(nii_view.handles.axial_axes, 'Ylim', ydata_ax); + end + if isfield(nii_view.handles, 'coronal_image') & ~isempty(nii_view.handles.coronal_image) + set(nii_view.handles.coronal_axes, 'Xlim', xdata_ax); + set(nii_view.handles.coronal_axes, 'Ylim', zdata_ax); + end + if isfield(nii_view.handles, 'sagittal_image') & ~isempty(nii_view.handles.sagittal_image) + set(nii_view.handles.sagittal_axes, 'Xlim', ydata_ax); + set(nii_view.handles.sagittal_axes, 'Ylim', zdata_ax); + end + + return % set_coordinates + + % ---------------------------------------------------------------- +function set_image_value(nii_view) + + % get coordinates of selected voxel and the image intensity there + % + sag = round(nii_view.slices.sag); + cor = round(nii_view.slices.cor); + axi = round(nii_view.slices.axi); + + if 0 % isfield(nii_view, 'disp') + img = nii_view.disp; + else + img = nii_view.nii.img; + end + + if nii_view.nii.hdr.dime.datatype == 128 + imgvalue = [double(img(sag, cor, axi, 1, nii_view.scanid)) double(img(sag, cor, axi, 2, nii_view.scanid)) double(img(sag, cor, axi, 3, nii_view.scanid))]; + set(nii_view.handles.imval, 'Value', imgvalue); + set(nii_view.handles.imval, 'String', sprintf('%7.4g %7.4g %7.4g', imgvalue)); + elseif nii_view.nii.hdr.dime.datatype == 511 + R = double(img(sag, cor, axi, 1, nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... + nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; + G = double(img(sag, cor, axi, 2, nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... + nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; + B = double(img(sag, cor, axi, 3, nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... + nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; + imgvalue = [double(img(sag, cor, axi, 1, nii_view.scanid)) double(img(sag, cor, axi, 2, nii_view.scanid)) double(img(sag, cor, axi, 3, nii_view.scanid))]; + set(nii_view.handles.imval, 'Value', imgvalue); + imgvalue = [R G B]; + set(nii_view.handles.imval, 'String', sprintf('%7.4g %7.4g %7.4g', imgvalue)); + else + imgvalue = double(img(sag, cor, axi, nii_view.scanid)); + set(nii_view.handles.imval, 'Value', imgvalue); + + if isnan(imgvalue) | imgvalue > nii_view.cbarminmax(2) + imgvalue = 0; end - nii_view = check_slices(nii_view); - - return % get_slider_position - - -%---------------------------------------------------------------- -function nii_view = update_imgXYZ(nii_view), - - nii_view.imgXYZ.vox = ... - [nii_view.slices.sag,nii_view.slices.cor,nii_view.slices.axi]; - nii_view.imgXYZ.mm = ... - (nii_view.imgXYZ.vox - nii_view.origin) .* nii_view.voxel_size; -% nii_view.imgXYZ.tal = mni2tal(nii_view.imgXYZ.mni); + set(nii_view.handles.imval, 'String', sprintf('%.6g', imgvalue)); + end + + % Now update the coordinates of the selected voxel + + nii_view = update_imgXYZ(nii_view); + + if get(nii_view.handles.coord, 'value') == 1 + sag = nii_view.imgXYZ.vox(1); + cor = nii_view.imgXYZ.vox(2); + axi = nii_view.imgXYZ.vox(3); + org = nii_view.origin; + elseif get(nii_view.handles.coord, 'value') == 2 + sag = nii_view.imgXYZ.mm(1); + cor = nii_view.imgXYZ.mm(2); + axi = nii_view.imgXYZ.mm(3); + org = [0 0 0]; + elseif get(nii_view.handles.coord, 'value') == 3 + sag = nii_view.imgXYZ.tal(1); + cor = nii_view.imgXYZ.tal(2); + axi = nii_view.imgXYZ.tal(3); + org = [0 0 0]; + end + + set(nii_view.handles.impos, 'Value', [sag, cor, axi]); + + if get(nii_view.handles.coord, 'value') == 1 + string = sprintf('%7.0f %7.0f %7.0f', sag, cor, axi); + org_str = sprintf('%7.0f %7.0f %7.0f', org(1), org(2), org(3)); + else + string = sprintf('%7.1f %7.1f %7.1f', sag, cor, axi); + org_str = sprintf('%7.1f %7.1f %7.1f', org(1), org(2), org(3)); + end + + set(nii_view.handles.impos, 'String', string); + set(nii_view.handles.origin, 'string', org_str); + + return % set_image_value + + % ---------------------------------------------------------------- +function nii_view = get_slice_position(nii_view, view) + + % obtain slices that is in correct unit, then update slices + % + slices = nii_view.slices; + + switch view + case 'sag' + currentpoint = get(nii_view.handles.sagittal_axes, 'CurrentPoint'); + slices.cor = currentpoint(1, 1); + slices.axi = currentpoint(1, 2); + case 'cor' + currentpoint = get(nii_view.handles.coronal_axes, 'CurrentPoint'); + slices.sag = currentpoint(1, 1); + slices.axi = currentpoint(1, 2); + case 'axi' + currentpoint = get(nii_view.handles.axial_axes, 'CurrentPoint'); + slices.sag = currentpoint(1, 1); + slices.cor = currentpoint(1, 2); + end + + % update nii_view.slices with the updated slices + % + nii_view.slices.axi = round(slices.axi); + nii_view.slices.cor = round(slices.cor); + nii_view.slices.sag = round(slices.sag); + + return % get_slice_position + + % ---------------------------------------------------------------- +function nii_view = get_slider_position(nii_view) + + [nii_view.slices.sag, nii_view.slices.cor, nii_view.slices.axi] = deal(0); + + if isfield(nii_view.handles, 'sagittal_slider') + if ishandle(nii_view.handles.sagittal_slider) + nii_view.slices.sag = ... + round(get(nii_view.handles.sagittal_slider, 'Value')); + end + end - return % update_imgXYZ + if isfield(nii_view.handles, 'coronal_slider') + if ishandle(nii_view.handles.coronal_slider) + nii_view.slices.cor = ... + round(nii_view.dims(2) - ... + get(nii_view.handles.coronal_slider, 'Value') + 1); + end + end + if isfield(nii_view.handles, 'axial_slider') + if ishandle(nii_view.handles.axial_slider) + nii_view.slices.axi = ... + round(get(nii_view.handles.axial_slider, 'Value')); + end + end -%---------------------------------------------------------------- -function nii_view = convert2voxel(nii_view,slices), + nii_view = check_slices(nii_view); - if get(nii_view.handles.coord,'value') == 1, + return % get_slider_position - % [slices.axi, slices.cor, slices.sag] are in vox - % - nii_view.slices.axi = round(slices.axi); - nii_view.slices.cor = round(slices.cor); - nii_view.slices.sag = round(slices.sag); + % ---------------------------------------------------------------- +function nii_view = update_imgXYZ(nii_view) - elseif get(nii_view.handles.coord,'value') == 2, + nii_view.imgXYZ.vox = ... + [nii_view.slices.sag, nii_view.slices.cor, nii_view.slices.axi]; + nii_view.imgXYZ.mm = ... + (nii_view.imgXYZ.vox - nii_view.origin) .* nii_view.voxel_size; + % nii_view.imgXYZ.tal = mni2tal(nii_view.imgXYZ.mni); - % [slices.axi, slices.cor, slices.sag] are in mm - % - xpix = nii_view.voxel_size(1); - ypix = nii_view.voxel_size(2); - zpix = nii_view.voxel_size(3); + return % update_imgXYZ - nii_view.slices.axi = round(slices.axi / zpix + nii_view.origin(3)); - nii_view.slices.cor = round(slices.cor / ypix + nii_view.origin(2)); - nii_view.slices.sag = round(slices.sag / xpix + nii_view.origin(1)); - elseif get(nii_view.handles.coord,'value') == 3, + % ---------------------------------------------------------------- +function nii_view = convert2voxel(nii_view, slices) - % [slices.axi, slices.cor, slices.sag] are in talairach - % - xpix = nii_view.voxel_size(1); - ypix = nii_view.voxel_size(2); - zpix = nii_view.voxel_size(3); + if get(nii_view.handles.coord, 'value') == 1 - xyz_tal = [slices.sag, slices.cor, slices.axi]; - xyz_mni = tal2mni(xyz_tal); - - nii_view.slices.axi = round(xyz_mni(3) / zpix + nii_view.origin(3)); - nii_view.slices.cor = round(xyz_mni(2) / ypix + nii_view.origin(2)); - nii_view.slices.sag = round(xyz_mni(1) / xpix + nii_view.origin(1)); - - end + % [slices.axi, slices.cor, slices.sag] are in vox + % + nii_view.slices.axi = round(slices.axi); + nii_view.slices.cor = round(slices.cor); + nii_view.slices.sag = round(slices.sag); - return % convert2voxel + elseif get(nii_view.handles.coord, 'value') == 2 + % [slices.axi, slices.cor, slices.sag] are in mm + % + xpix = nii_view.voxel_size(1); + ypix = nii_view.voxel_size(2); + zpix = nii_view.voxel_size(3); -%---------------------------------------------------------------- -function nii_view = check_slices(nii_view), + nii_view.slices.axi = round(slices.axi / zpix + nii_view.origin(3)); + nii_view.slices.cor = round(slices.cor / ypix + nii_view.origin(2)); + nii_view.slices.sag = round(slices.sag / xpix + nii_view.origin(1)); + elseif get(nii_view.handles.coord, 'value') == 3 - img = nii_view.nii.img; - - [ SagSize, CorSize, AxiSize, TimeSize ] = size(img); - if nii_view.slices.sag > SagSize, nii_view.slices.sag = SagSize; end; - if nii_view.slices.sag < 1, nii_view.slices.sag = 1; end; - if nii_view.slices.cor > CorSize, nii_view.slices.cor = CorSize; end; - if nii_view.slices.cor < 1, nii_view.slices.cor = 1; end; - if nii_view.slices.axi > AxiSize, nii_view.slices.axi = AxiSize; end; - if nii_view.slices.axi < 1, nii_view.slices.axi = 1; end; - if nii_view.scanid > TimeSize, nii_view.scanid = TimeSize; end; - if nii_view.scanid < 1, nii_view.scanid = 1; end; - - return % check_slices - - -%---------------------------------------------------------------- -% -% keep this function small, since it will be called for every click -% + % [slices.axi, slices.cor, slices.sag] are in talairach + % + xpix = nii_view.voxel_size(1); + ypix = nii_view.voxel_size(2); + zpix = nii_view.voxel_size(3); + + xyz_tal = [slices.sag, slices.cor, slices.axi]; + xyz_mni = tal2mni(xyz_tal); + + nii_view.slices.axi = round(xyz_mni(3) / zpix + nii_view.origin(3)); + nii_view.slices.cor = round(xyz_mni(2) / ypix + nii_view.origin(2)); + nii_view.slices.sag = round(xyz_mni(1) / xpix + nii_view.origin(1)); + + end + + return % convert2voxel + + % ---------------------------------------------------------------- +function nii_view = check_slices(nii_view) + + img = nii_view.nii.img; + + [SagSize, CorSize, AxiSize, TimeSize] = size(img); + if nii_view.slices.sag > SagSize + nii_view.slices.sag = SagSize; + end + if nii_view.slices.sag < 1 + nii_view.slices.sag = 1; + end + if nii_view.slices.cor > CorSize + nii_view.slices.cor = CorSize; + end + if nii_view.slices.cor < 1 + nii_view.slices.cor = 1; + end + if nii_view.slices.axi > AxiSize + nii_view.slices.axi = AxiSize; + end + if nii_view.slices.axi < 1 + nii_view.slices.axi = 1; + end + if nii_view.scanid > TimeSize + nii_view.scanid = TimeSize; + end + if nii_view.scanid < 1 + nii_view.scanid = 1; + end + + return % check_slices + + % ---------------------------------------------------------------- + % + % keep this function small, since it will be called for every click + % function nii_view = update_nii_view(nii_view) - % add imgXYZ into nii_view struct - % - nii_view = check_slices(nii_view); - nii_view = update_imgXYZ(nii_view); - - % update xhair - % - p_axi = nii_view.imgXYZ.vox([1 2]); - p_cor = nii_view.imgXYZ.vox([1 3]); - p_sag = nii_view.imgXYZ.vox([2 3]); + % add imgXYZ into nii_view struct + % + nii_view = check_slices(nii_view); + nii_view = update_imgXYZ(nii_view); - nii_view.axi_xhair = ... - rri_xhair(p_axi, nii_view.axi_xhair, nii_view.handles.axial_axes); + % update xhair + % + p_axi = nii_view.imgXYZ.vox([1 2]); + p_cor = nii_view.imgXYZ.vox([1 3]); + p_sag = nii_view.imgXYZ.vox([2 3]); - nii_view.cor_xhair = ... - rri_xhair(p_cor, nii_view.cor_xhair, nii_view.handles.coronal_axes); + nii_view.axi_xhair = ... + rri_xhair(p_axi, nii_view.axi_xhair, nii_view.handles.axial_axes); - nii_view.sag_xhair = ... - rri_xhair(p_sag, nii_view.sag_xhair, nii_view.handles.sagittal_axes); + nii_view.cor_xhair = ... + rri_xhair(p_cor, nii_view.cor_xhair, nii_view.handles.coronal_axes); - setappdata(nii_view.fig, 'nii_view', nii_view); - set_image_value(nii_view); + nii_view.sag_xhair = ... + rri_xhair(p_sag, nii_view.sag_xhair, nii_view.handles.sagittal_axes); - return; % update_nii_view + setappdata(nii_view.fig, 'nii_view', nii_view); + set_image_value(nii_view); + return % update_nii_view -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function hist_plot(fig) - nii_view = getappdata(fig,'nii_view'); - - if isfield(nii_view, 'disp') - img = nii_view.disp; - else - img = nii_view.nii.img; - end + nii_view = getappdata(fig, 'nii_view'); - img = double(img(:)); + if isfield(nii_view, 'disp') + img = nii_view.disp; + else + img = nii_view.nii.img; + end - if length(unique(round(img))) == length(unique(img)) - is_integer = 1; - range = max(img) - min(img) + 1; - figure; hist(img, range); - set(gca, 'xlim', [-range/5, max(img)]); - else - is_integer = 0; - figure; hist(img); - end + img = double(img(:)); - xlabel('Voxel Intensity'); - ylabel('Voxel Numbers for Each Intensity'); - set(gcf, 'NumberTitle','off','Name','Histogram Plot'); + if length(unique(round(img))) == length(unique(img)) + is_integer = 1; + range = max(img) - min(img) + 1; + figure; + hist(img, range); + set(gca, 'xlim', [-range / 5, max(img)]); + else + is_integer = 0; + figure; + hist(img); + end - return; % hist_plot + xlabel('Voxel Intensity'); + ylabel('Voxel Numbers for Each Intensity'); + set(gcf, 'NumberTitle', 'off', 'Name', 'Histogram Plot'); + return % hist_plot -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function hist_eq(fig) - nii_view = getappdata(fig,'nii_view'); - - old_pointer = get(fig,'Pointer'); - set(fig,'Pointer','watch'); - - if get(nii_view.handles.hist_eq,'value') - max_img = double(max(nii_view.nii.img(:))); - tmp = double(nii_view.nii.img) / max_img; % normalize for histeq - tmp = histeq(tmp(:)); - nii_view.disp = reshape(tmp, size(nii_view.nii.img)); - min_disp = min(nii_view.disp(:)); - nii_view.disp = (nii_view.disp - min_disp); % range having eq hist - nii_view.disp = nii_view.disp * max_img / max(nii_view.disp(:)); - nii_view.disp = single(nii_view.disp); - else - if isfield(nii_view, 'disp') - nii_view.disp = nii_view.nii.img; - else - set(fig,'Pointer',old_pointer); - return; - end - end - - % update axial view - % - img_slice = squeeze(double(nii_view.disp(:,:,nii_view.slices.axi))); - h1 = nii_view.handles.axial_image; - set(h1, 'cdata', double(img_slice)'); + nii_view = getappdata(fig, 'nii_view'); + + old_pointer = get(fig, 'Pointer'); + set(fig, 'Pointer', 'watch'); + + if get(nii_view.handles.hist_eq, 'value') + max_img = double(max(nii_view.nii.img(:))); + tmp = double(nii_view.nii.img) / max_img; % normalize for histeq + tmp = histeq(tmp(:)); + nii_view.disp = reshape(tmp, size(nii_view.nii.img)); + min_disp = min(nii_view.disp(:)); + nii_view.disp = (nii_view.disp - min_disp); % range having eq hist + nii_view.disp = nii_view.disp * max_img / max(nii_view.disp(:)); + nii_view.disp = single(nii_view.disp); + else + if isfield(nii_view, 'disp') + nii_view.disp = nii_view.nii.img; + else + set(fig, 'Pointer', old_pointer); + return + end + end - % update coronal view - % - img_slice = squeeze(double(nii_view.disp(:,nii_view.slices.cor,:))); - h1 = nii_view.handles.coronal_image; - set(h1, 'cdata', double(img_slice)'); + % update axial view + % + img_slice = squeeze(double(nii_view.disp(:, :, nii_view.slices.axi))); + h1 = nii_view.handles.axial_image; + set(h1, 'cdata', double(img_slice)'); - % update sagittal view - % - img_slice = squeeze(double(nii_view.disp(nii_view.slices.sag,:,:))); + % update coronal view + % + img_slice = squeeze(double(nii_view.disp(:, nii_view.slices.cor, :))); + h1 = nii_view.handles.coronal_image; + set(h1, 'cdata', double(img_slice)'); - h1 = nii_view.handles.sagittal_image; - set(h1, 'cdata', double(img_slice)'); + % update sagittal view + % + img_slice = squeeze(double(nii_view.disp(nii_view.slices.sag, :, :))); - % remove disp field if un-check 'histeq' button - % - if ~get(nii_view.handles.hist_eq,'value') & isfield(nii_view, 'disp') - nii_view = rmfield(nii_view, 'disp'); - end + h1 = nii_view.handles.sagittal_image; + set(h1, 'cdata', double(img_slice)'); - update_nii_view(nii_view); + % remove disp field if un-check 'histeq' button + % + if ~get(nii_view.handles.hist_eq, 'value') & isfield(nii_view, 'disp') + nii_view = rmfield(nii_view, 'disp'); + end - set(fig,'Pointer',old_pointer); + update_nii_view(nii_view); - return; % hist_eq + set(fig, 'Pointer', old_pointer); + return % hist_eq -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function [top1_label, top2_label, side1_label, side2_label] = ... - dir_label(fig, top_ax, front_ax, side_ax) - - nii_view = getappdata(fig,'nii_view'); - - top_pos = get(top_ax,'position'); - front_pos = get(front_ax,'position'); - side_pos = get(side_ax,'position'); - - top_gap_x = (side_pos(1)-top_pos(1)-top_pos(3)) / (2*top_pos(3)); - top_gap_y = (front_pos(2)-top_pos(2)-top_pos(4)) / (2*top_pos(4)); - side_gap_x = (side_pos(1)-top_pos(1)-top_pos(3)) / (2*side_pos(3)); - side_gap_y = (front_pos(2)-top_pos(2)-top_pos(4)) / (2*side_pos(4)); - - top1_label_pos = [0, 1]; % rot0 - top2_label_pos = [1, 0]; % rot90 - side1_label_pos = [1, - side_gap_y]; % rot0 - side2_label_pos = [0, 0]; % rot90 - - if isempty(nii_view) - axes(top_ax); - top1_label = text(double(top1_label_pos(1)),double(top1_label_pos(2)), ... - '== X =>', ... - 'vertical', 'bottom', ... - 'unit', 'normal', 'fontsize', 8); - - axes(top_ax); - top2_label = text(double(top2_label_pos(1)),double(top2_label_pos(2)), ... - '== Y =>', ... - 'rotation', 90, 'vertical', 'top', ... - 'unit', 'normal', 'fontsize', 8); - - axes(side_ax); - side1_label = text(double(side1_label_pos(1)),double(side1_label_pos(2)), ... - '<= Y ==', ... - 'horizontal', 'right', 'vertical', 'top', ... - 'unit', 'normal', 'fontsize', 8); - - axes(side_ax); - side2_label = text(double(side2_label_pos(1)),double(side2_label_pos(2)), ... - '== Z =>', ... - 'rotation', 90, 'vertical', 'bottom', ... - 'unit', 'normal', 'fontsize', 8); - else - top1_label = nii_view.handles.top1_label; - top2_label = nii_view.handles.top2_label; - side1_label = nii_view.handles.side1_label; - side2_label = nii_view.handles.side2_label; - - set(top1_label, 'position', [top1_label_pos 0]); - set(top2_label, 'position', [top2_label_pos 0]); - set(side1_label, 'position', [side1_label_pos 0]); - set(side2_label, 'position', [side2_label_pos 0]); - end - - return; % dir_label - - -%---------------------------------------------------------------- -function update_enable(h, opt); - - nii_view = getappdata(h,'nii_view'); - handles = nii_view.handles; - - if isfield(opt,'enablecursormove') - if opt.enablecursormove - v = 'on'; - else - v = 'off'; - end - - set(handles.Timposcur, 'visible', v); - set(handles.imposcur, 'visible', v); - set(handles.Timvalcur, 'visible', v); - set(handles.imvalcur, 'visible', v); - end + dir_label(fig, top_ax, front_ax, side_ax) + + nii_view = getappdata(fig, 'nii_view'); + + top_pos = get(top_ax, 'position'); + front_pos = get(front_ax, 'position'); + side_pos = get(side_ax, 'position'); + + top_gap_x = (side_pos(1) - top_pos(1) - top_pos(3)) / (2 * top_pos(3)); + top_gap_y = (front_pos(2) - top_pos(2) - top_pos(4)) / (2 * top_pos(4)); + side_gap_x = (side_pos(1) - top_pos(1) - top_pos(3)) / (2 * side_pos(3)); + side_gap_y = (front_pos(2) - top_pos(2) - top_pos(4)) / (2 * side_pos(4)); + + top1_label_pos = [0, 1]; % rot0 + top2_label_pos = [1, 0]; % rot90 + side1_label_pos = [1, -side_gap_y]; % rot0 + side2_label_pos = [0, 0]; % rot90 + + if isempty(nii_view) + axes(top_ax); + top1_label = text(double(top1_label_pos(1)), double(top1_label_pos(2)), ... + '== X =>', ... + 'vertical', 'bottom', ... + 'unit', 'normal', 'fontsize', 8); + + axes(top_ax); + top2_label = text(double(top2_label_pos(1)), double(top2_label_pos(2)), ... + '== Y =>', ... + 'rotation', 90, 'vertical', 'top', ... + 'unit', 'normal', 'fontsize', 8); + + axes(side_ax); + side1_label = text(double(side1_label_pos(1)), double(side1_label_pos(2)), ... + '<= Y ==', ... + 'horizontal', 'right', 'vertical', 'top', ... + 'unit', 'normal', 'fontsize', 8); + + axes(side_ax); + side2_label = text(double(side2_label_pos(1)), double(side2_label_pos(2)), ... + '== Z =>', ... + 'rotation', 90, 'vertical', 'bottom', ... + 'unit', 'normal', 'fontsize', 8); + else + top1_label = nii_view.handles.top1_label; + top2_label = nii_view.handles.top2_label; + side1_label = nii_view.handles.side1_label; + side2_label = nii_view.handles.side2_label; + + set(top1_label, 'position', [top1_label_pos 0]); + set(top2_label, 'position', [top2_label_pos 0]); + set(side1_label, 'position', [side1_label_pos 0]); + set(side2_label, 'position', [side2_label_pos 0]); + end + + return % dir_label + + % ---------------------------------------------------------------- +function update_enable(h, opt) + + nii_view = getappdata(h, 'nii_view'); + handles = nii_view.handles; + + if isfield(opt, 'enablecursormove') + if opt.enablecursormove + v = 'on'; + else + v = 'off'; + end - if isfield(opt,'enableviewpoint') - if opt.enableviewpoint - v = 'on'; - else - v = 'off'; - end + set(handles.Timposcur, 'visible', v); + set(handles.imposcur, 'visible', v); + set(handles.Timvalcur, 'visible', v); + set(handles.imvalcur, 'visible', v); + end - set(handles.Timpos, 'visible', v); - set(handles.impos, 'visible', v); - set(handles.Timval, 'visible', v); - set(handles.imval, 'visible', v); - end + if isfield(opt, 'enableviewpoint') + if opt.enableviewpoint + v = 'on'; + else + v = 'off'; + end - if isfield(opt,'enableorigin') - if opt.enableorigin - v = 'on'; - else - v = 'off'; - end + set(handles.Timpos, 'visible', v); + set(handles.impos, 'visible', v); + set(handles.Timval, 'visible', v); + set(handles.imval, 'visible', v); + end - set(handles.Torigin, 'visible', v); - set(handles.origin, 'visible', v); - end + if isfield(opt, 'enableorigin') + if opt.enableorigin + v = 'on'; + else + v = 'off'; + end - if isfield(opt,'enableunit') - if opt.enableunit - v = 'on'; - else - v = 'off'; - end + set(handles.Torigin, 'visible', v); + set(handles.origin, 'visible', v); + end - set(handles.Tcoord, 'visible', v); - set(handles.coord_frame, 'visible', v); - set(handles.coord, 'visible', v); - end + if isfield(opt, 'enableunit') + if opt.enableunit + v = 'on'; + else + v = 'off'; + end - if isfield(opt,'enablecrosshair') - if opt.enablecrosshair - v = 'on'; - else - v = 'off'; - end + set(handles.Tcoord, 'visible', v); + set(handles.coord_frame, 'visible', v); + set(handles.coord, 'visible', v); + end - set(handles.Txhair, 'visible', v); - set(handles.xhair_color, 'visible', v); - set(handles.xhair, 'visible', v); - end + if isfield(opt, 'enablecrosshair') + if opt.enablecrosshair + v = 'on'; + else + v = 'off'; + end - if isfield(opt,'enablehistogram') - if opt.enablehistogram - v = 'on'; - vv = 'off'; - else - v = 'off'; - vv = 'on'; - end + set(handles.Txhair, 'visible', v); + set(handles.xhair_color, 'visible', v); + set(handles.xhair, 'visible', v); + end - set(handles.Tcoord, 'visible', vv); - set(handles.coord_frame, 'visible', vv); - set(handles.coord, 'visible', vv); + if isfield(opt, 'enablehistogram') + if opt.enablehistogram + v = 'on'; + vv = 'off'; + else + v = 'off'; + vv = 'on'; + end - set(handles.Thist, 'visible', v); - set(handles.hist_frame, 'visible', v); - set(handles.hist_eq, 'visible', v); - set(handles.hist_plot, 'visible', v); - end + set(handles.Tcoord, 'visible', vv); + set(handles.coord_frame, 'visible', vv); + set(handles.coord, 'visible', vv); - if isfield(opt,'enablecolormap') - if opt.enablecolormap - v = 'on'; - else - v = 'off'; - end + set(handles.Thist, 'visible', v); + set(handles.hist_frame, 'visible', v); + set(handles.hist_eq, 'visible', v); + set(handles.hist_plot, 'visible', v); + end - set(handles.Tcolor, 'visible', v); - set(handles.color_frame, 'visible', v); - set(handles.neg_color, 'visible', v); - set(handles.colorindex, 'visible', v); - end + if isfield(opt, 'enablecolormap') + if opt.enablecolormap + v = 'on'; + else + v = 'off'; + end - if isfield(opt,'enablecontrast') - if opt.enablecontrast - v = 'on'; - else - v = 'off'; - end + set(handles.Tcolor, 'visible', v); + set(handles.color_frame, 'visible', v); + set(handles.neg_color, 'visible', v); + set(handles.colorindex, 'visible', v); + end - set(handles.Tcontrast, 'visible', v); - set(handles.contrast_frame, 'visible', v); - set(handles.contrast_def, 'visible', v); - set(handles.contrast, 'visible', v); - end + if isfield(opt, 'enablecontrast') + if opt.enablecontrast + v = 'on'; + else + v = 'off'; + end - if isfield(opt,'enablebrightness') - if opt.enablebrightness - v = 'on'; - else - v = 'off'; - end + set(handles.Tcontrast, 'visible', v); + set(handles.contrast_frame, 'visible', v); + set(handles.contrast_def, 'visible', v); + set(handles.contrast, 'visible', v); + end - set(handles.Tbrightness, 'visible', v); - set(handles.brightness_frame, 'visible', v); - set(handles.brightness_def, 'visible', v); - set(handles.brightness, 'visible', v); - end + if isfield(opt, 'enablebrightness') + if opt.enablebrightness + v = 'on'; + else + v = 'off'; + end - if isfield(opt,'enabledirlabel') - if opt.enabledirlabel - v = 'on'; - else - v = 'off'; - end + set(handles.Tbrightness, 'visible', v); + set(handles.brightness_frame, 'visible', v); + set(handles.brightness_def, 'visible', v); + set(handles.brightness, 'visible', v); + end - set(handles.top1_label, 'visible', v); - set(handles.top2_label, 'visible', v); - set(handles.side1_label, 'visible', v); - set(handles.side2_label, 'visible', v); - end + if isfield(opt, 'enabledirlabel') + if opt.enabledirlabel + v = 'on'; + else + v = 'off'; + end - if isfield(opt,'enableslider') - if opt.enableslider - v = 'on'; - else - v = 'off'; - end + set(handles.top1_label, 'visible', v); + set(handles.top2_label, 'visible', v); + set(handles.side1_label, 'visible', v); + set(handles.side2_label, 'visible', v); + end - if isfield(handles,'sagittal_slider') & ishandle(handles.sagittal_slider) - set(handles.sagittal_slider, 'visible', v); - end + if isfield(opt, 'enableslider') + if opt.enableslider + v = 'on'; + else + v = 'off'; + end - if isfield(handles,'coronal_slider') & ishandle(handles.coronal_slider) - set(handles.coronal_slider, 'visible', v); - end + if isfield(handles, 'sagittal_slider') & ishandle(handles.sagittal_slider) + set(handles.sagittal_slider, 'visible', v); + end - if isfield(handles,'axial_slider') & ishandle(handles.axial_slider) - set(handles.axial_slider, 'visible', v); - end - end + if isfield(handles, 'coronal_slider') & ishandle(handles.coronal_slider) + set(handles.coronal_slider, 'visible', v); + end - return; % update_enable + if isfield(handles, 'axial_slider') & ishandle(handles.axial_slider) + set(handles.axial_slider, 'visible', v); + end + end + return % update_enable -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_usepanel(fig, usepanel) - if isempty(usepanel) - return; - end - - if usepanel - opt.enablecursormove = 1; - opt.enableviewpoint = 1; - opt.enableorigin = 1; - opt.enableunit = 1; - opt.enablecrosshair = 1; -% opt.enablehistogram = 1; - opt.enablecolormap = 1; - opt.enablecontrast = 1; - opt.enablebrightness = 1; - else - opt.enablecursormove = 0; - opt.enableviewpoint = 0; - opt.enableorigin = 0; - opt.enableunit = 0; - opt.enablecrosshair = 0; -% opt.enablehistogram = 0; - opt.enablecolormap = 0; - opt.enablecontrast = 0; - opt.enablebrightness = 0; - end - - update_enable(fig, opt); - - nii_view = getappdata(fig,'nii_view'); - nii_view.usepanel = usepanel; - setappdata(fig,'nii_view',nii_view); - - return; % update_usepanel - - -%---------------------------------------------------------------- + if isempty(usepanel) + return + end + + if usepanel + opt.enablecursormove = 1; + opt.enableviewpoint = 1; + opt.enableorigin = 1; + opt.enableunit = 1; + opt.enablecrosshair = 1; + % opt.enablehistogram = 1; + opt.enablecolormap = 1; + opt.enablecontrast = 1; + opt.enablebrightness = 1; + else + opt.enablecursormove = 0; + opt.enableviewpoint = 0; + opt.enableorigin = 0; + opt.enableunit = 0; + opt.enablecrosshair = 0; + % opt.enablehistogram = 0; + opt.enablecolormap = 0; + opt.enablecontrast = 0; + opt.enablebrightness = 0; + end + + update_enable(fig, opt); + + nii_view = getappdata(fig, 'nii_view'); + nii_view.usepanel = usepanel; + setappdata(fig, 'nii_view', nii_view); + + return % update_usepanel + + % ---------------------------------------------------------------- function update_usecrosshair(fig, usecrosshair) - if isempty(usecrosshair) - return; - end - - if usecrosshair - v=1; - else - v=2; - end + if isempty(usecrosshair) + return + end - nii_view = getappdata(fig,'nii_view'); - set(nii_view.handles.xhair,'value',v); + if usecrosshair + v = 1; + else + v = 2; + end - opt.command = 'crosshair'; - view_nii(fig, opt); + nii_view = getappdata(fig, 'nii_view'); + set(nii_view.handles.xhair, 'value', v); - return; % update_usecrosshair + opt.command = 'crosshair'; + view_nii(fig, opt); + return % update_usecrosshair -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_usestretch(fig, usestretch) - nii_view = getappdata(fig,'nii_view'); - - handles = nii_view.handles; - fig = nii_view.fig; - area = nii_view.area; - vol_size = nii_view.voxel_size .* nii_view.dims; - - % Three Axes & label - % - [top_ax, front_ax, side_ax] = ... - create_ax(fig, area, vol_size, usestretch); - - dir_label(fig, top_ax, front_ax, side_ax); - - top_pos = get(top_ax,'position'); - front_pos = get(front_ax,'position'); - side_pos = get(side_ax,'position'); - - % Sagittal Slider - % - x = side_pos(1); - y = top_pos(2) + top_pos(4); - w = side_pos(3); - h = (front_pos(2) - y) / 2; - y = y + h; - pos = [x y w h]; - - if isfield(handles,'sagittal_slider') & ishandle(handles.sagittal_slider) - set(handles.sagittal_slider,'position',pos); - end - - % Coronal Slider - % - x = top_pos(1); - y = top_pos(2) + top_pos(4); - w = top_pos(3); - h = (front_pos(2) - y) / 2; - y = y + h; - pos = [x y w h]; - - if isfield(handles,'coronal_slider') & ishandle(handles.coronal_slider) - set(handles.coronal_slider,'position',pos); - end - - % Axial Slider - % - x = top_pos(1); - y = area(2); - w = top_pos(3); - h = top_pos(2) - y; - pos = [x y w h]; - - if isfield(handles,'axial_slider') & ishandle(handles.axial_slider) - set(handles.axial_slider,'position',pos); - end - - % plot info view - % -% info_pos = [side_pos([1,3]); top_pos([2,4])]; -% info_pos = info_pos(:); - gap = side_pos(1)-(top_pos(1)+top_pos(3)); - info_pos(1) = side_pos(1) + gap; - info_pos(2) = area(2); - info_pos(3) = side_pos(3) - gap; - info_pos(4) = top_pos(2) + top_pos(4) - area(2) - gap; - - num_inputline = 10; - inputline_space =info_pos(4) / num_inputline; - - - % Image Intensity Value at Cursor - % - x = info_pos(1); - y = info_pos(2); - w = info_pos(3)*0.5; - h = inputline_space*0.6; - - pos = [x y w h]; - set(handles.Timvalcur,'position',pos); - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.imvalcur,'position',pos); - - % Position at Cursor - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.Timposcur,'position',pos); - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.imposcur,'position',pos); - - % Image Intensity Value at Mouse Click - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.Timval,'position',pos); - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.imval,'position',pos); - - % Viewpoint Position at Mouse Click - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.Timpos,'position',pos); - - x = x + w + 0.005; - y = y - 0.008; - w = info_pos(3)*0.5; - h = inputline_space*0.9; - - pos = [x y w h]; - set(handles.impos,'position',pos); - - % Origin Position - % - x = info_pos(1); - y = y + inputline_space*1.2; - w = info_pos(3)*0.5; - h = inputline_space*0.6; - - pos = [x y w h]; - set(handles.Torigin,'position',pos); - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.origin,'position',pos); + nii_view = getappdata(fig, 'nii_view'); + + handles = nii_view.handles; + fig = nii_view.fig; + area = nii_view.area; + vol_size = nii_view.voxel_size .* nii_view.dims; + + % Three Axes & label + % + [top_ax, front_ax, side_ax] = ... + create_ax(fig, area, vol_size, usestretch); + + dir_label(fig, top_ax, front_ax, side_ax); + + top_pos = get(top_ax, 'position'); + front_pos = get(front_ax, 'position'); + side_pos = get(side_ax, 'position'); + + % Sagittal Slider + % + x = side_pos(1); + y = top_pos(2) + top_pos(4); + w = side_pos(3); + h = (front_pos(2) - y) / 2; + y = y + h; + pos = [x y w h]; + + if isfield(handles, 'sagittal_slider') & ishandle(handles.sagittal_slider) + set(handles.sagittal_slider, 'position', pos); + end + + % Coronal Slider + % + x = top_pos(1); + y = top_pos(2) + top_pos(4); + w = top_pos(3); + h = (front_pos(2) - y) / 2; + y = y + h; + pos = [x y w h]; + + if isfield(handles, 'coronal_slider') & ishandle(handles.coronal_slider) + set(handles.coronal_slider, 'position', pos); + end + + % Axial Slider + % + x = top_pos(1); + y = area(2); + w = top_pos(3); + h = top_pos(2) - y; + pos = [x y w h]; + + if isfield(handles, 'axial_slider') & ishandle(handles.axial_slider) + set(handles.axial_slider, 'position', pos); + end + + % plot info view + % + % info_pos = [side_pos([1,3]); top_pos([2,4])]; + % info_pos = info_pos(:); + gap = side_pos(1) - (top_pos(1) + top_pos(3)); + info_pos(1) = side_pos(1) + gap; + info_pos(2) = area(2); + info_pos(3) = side_pos(3) - gap; + info_pos(4) = top_pos(2) + top_pos(4) - area(2) - gap; + + num_inputline = 10; + inputline_space = info_pos(4) / num_inputline; + + % Image Intensity Value at Cursor + % + x = info_pos(1); + y = info_pos(2); + w = info_pos(3) * 0.5; + h = inputline_space * 0.6; + + pos = [x y w h]; + set(handles.Timvalcur, 'position', pos); + + x = x + w; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + set(handles.imvalcur, 'position', pos); + + % Position at Cursor + % + x = info_pos(1); + y = y + inputline_space; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + set(handles.Timposcur, 'position', pos); + + x = x + w; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + set(handles.imposcur, 'position', pos); + + % Image Intensity Value at Mouse Click + % + x = info_pos(1); + y = y + inputline_space; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + set(handles.Timval, 'position', pos); + + x = x + w; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + set(handles.imval, 'position', pos); + + % Viewpoint Position at Mouse Click + % + x = info_pos(1); + y = y + inputline_space; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + set(handles.Timpos, 'position', pos); + + x = x + w + 0.005; + y = y - 0.008; + w = info_pos(3) * 0.5; + h = inputline_space * 0.9; + + pos = [x y w h]; + set(handles.impos, 'position', pos); + + % Origin Position + % + x = info_pos(1); + y = y + inputline_space * 1.2; + w = info_pos(3) * 0.5; + h = inputline_space * 0.6; + + pos = [x y w h]; + set(handles.Torigin, 'position', pos); + + x = x + w; + w = info_pos(3) * 0.5; + + pos = [x y w h]; + set(handles.origin, 'position', pos); + + if 0 + % Axes Unit + % + x = info_pos(1); + y = y + inputline_space; + w = info_pos(3) * 0.5; -if 0 - % Axes Unit - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; + pos = [x y w h]; + set(handles.Tcoord, 'position', pos); - pos = [x y w h]; - set(handles.Tcoord,'position',pos); + x = x + w + 0.005; + w = info_pos(3) * 0.5 - 0.005; - x = x + w + 0.005; - w = info_pos(3)*0.5 - 0.005; + pos = [x y w h]; + set(handles.coord, 'position', pos); + end - pos = [x y w h]; - set(handles.coord,'position',pos); -end + % Crosshair + % + x = info_pos(1); + y = y + inputline_space; + w = info_pos(3) * 0.4; - % Crosshair - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.4; + pos = [x y w h]; + set(handles.Txhair, 'position', pos); - pos = [x y w h]; - set(handles.Txhair,'position',pos); + x = info_pos(1) + info_pos(3) * 0.5; + w = info_pos(3) * 0.2; + h = inputline_space * 0.7; - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.2; - h = inputline_space*0.7; + pos = [x y w h]; + set(handles.xhair_color, 'position', pos); - pos = [x y w h]; - set(handles.xhair_color,'position',pos); + x = info_pos(1) + info_pos(3) * 0.7; + w = info_pos(3) * 0.3; - x = info_pos(1) + info_pos(3)*0.7; - w = info_pos(3)*0.3; + pos = [x y w h]; + set(handles.xhair, 'position', pos); - pos = [x y w h]; - set(handles.xhair,'position',pos); + % Histogram & Color + % + x = info_pos(1); + w = info_pos(3) * 0.45; + h = inputline_space * 1.5; + pos = [x, y + inputline_space * 0.9, w, h]; + set(handles.hist_frame, 'position', pos); + set(handles.coord_frame, 'position', pos); - % Histogram & Color - % - x = info_pos(1); - w = info_pos(3)*0.45; - h = inputline_space * 1.5; - pos = [x, y+inputline_space*0.9, w, h]; - set(handles.hist_frame,'position',pos); - set(handles.coord_frame,'position',pos); + x = info_pos(1) + info_pos(3) * 0.475; + w = info_pos(3) * 0.525; + h = inputline_space * 1.5; - x = info_pos(1) + info_pos(3)*0.475; - w = info_pos(3)*0.525; - h = inputline_space * 1.5; + pos = [x, y + inputline_space * 0.9, w, h]; + set(handles.color_frame, 'position', pos); - pos = [x, y+inputline_space*0.9, w, h]; - set(handles.color_frame,'position',pos); + x = info_pos(1) + info_pos(3) * 0.025; + y = y + inputline_space * 1.2; + w = info_pos(3) * 0.2; + h = inputline_space * 0.7; - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space*1.2; - w = info_pos(3)*0.2; - h = inputline_space*0.7; + pos = [x y w h]; + set(handles.hist_eq, 'position', pos); - pos = [x y w h]; - set(handles.hist_eq,'position',pos); + x = x + w; + w = info_pos(3) * 0.2; - x = x + w; - w = info_pos(3)*0.2; + pos = [x y w h]; + set(handles.hist_plot, 'position', pos); - pos = [x y w h]; - set(handles.hist_plot,'position',pos); + x = info_pos(1) + info_pos(3) * 0.025; + w = info_pos(3) * 0.4; - x = info_pos(1) + info_pos(3)*0.025; - w = info_pos(3)*0.4; + pos = [x y w h]; + set(handles.coord, 'position', pos); - pos = [x y w h]; - set(handles.coord,'position',pos); + x = info_pos(1) + info_pos(3) * 0.5; + w = info_pos(3) * 0.2; + pos = [x y w h]; + set(handles.neg_color, 'position', pos); - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.2; - pos = [x y w h]; - set(handles.neg_color,'position',pos); + x = info_pos(1) + info_pos(3) * 0.7; + w = info_pos(3) * 0.275; - x = info_pos(1) + info_pos(3)*0.7; - w = info_pos(3)*0.275; + pos = [x y w h]; + set(handles.colorindex, 'position', pos); - pos = [x y w h]; - set(handles.colorindex,'position',pos); + x = info_pos(1) + info_pos(3) * 0.1; + y = y + inputline_space; + w = info_pos(3) * 0.28; + h = inputline_space * 0.6; - x = info_pos(1) + info_pos(3)*0.1; - y = y + inputline_space; - w = info_pos(3)*0.28; - h = inputline_space*0.6; + pos = [x y w h]; + set(handles.Thist, 'position', pos); + set(handles.Tcoord, 'position', pos); - pos = [x y w h]; - set(handles.Thist,'position',pos); - set(handles.Tcoord,'position',pos); + x = info_pos(1) + info_pos(3) * 0.60; + w = info_pos(3) * 0.28; - x = info_pos(1) + info_pos(3)*0.60; - w = info_pos(3)*0.28; + pos = [x y w h]; + set(handles.Tcolor, 'position', pos); - pos = [x y w h]; - set(handles.Tcolor,'position',pos); + % Contrast Frame + % + x = info_pos(1); + w = info_pos(3) * 0.45; + h = inputline_space * 2; - % Contrast Frame - % - x = info_pos(1); - w = info_pos(3)*0.45; - h = inputline_space * 2; + pos = [x, y + inputline_space * 0.8, w, h]; + set(handles.contrast_frame, 'position', pos); - pos = [x, y+inputline_space*0.8, w, h]; - set(handles.contrast_frame,'position',pos); + % Brightness Frame + % + x = info_pos(1) + info_pos(3) * 0.475; + w = info_pos(3) * 0.525; - % Brightness Frame - % - x = info_pos(1) + info_pos(3)*0.475; - w = info_pos(3)*0.525; + pos = [x, y + inputline_space * 0.8, w, h]; + set(handles.brightness_frame, 'position', pos); - pos = [x, y+inputline_space*0.8, w, h]; - set(handles.brightness_frame,'position',pos); + % Contrast + % + x = info_pos(1) + info_pos(3) * 0.025; + y = y + inputline_space; + w = info_pos(3) * 0.4; + h = inputline_space * 0.6; - % Contrast - % - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space; - w = info_pos(3)*0.4; - h = inputline_space*0.6; + pos = [x y w h]; + set(handles.contrast, 'position', pos); - pos = [x y w h]; - set(handles.contrast,'position',pos); + % Brightness + % + x = info_pos(1) + info_pos(3) * 0.5; + w = info_pos(3) * 0.475; - % Brightness - % - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.475; + pos = [x y w h]; + set(handles.brightness, 'position', pos); - pos = [x y w h]; - set(handles.brightness,'position',pos); + % Contrast text/def + % + x = info_pos(1) + info_pos(3) * 0.025; + y = y + inputline_space; + w = info_pos(3) * 0.22; - % Contrast text/def - % - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space; - w = info_pos(3)*0.22; + pos = [x y w h]; + set(handles.Tcontrast, 'position', pos); - pos = [x y w h]; - set(handles.Tcontrast,'position',pos); + x = x + w; + w = info_pos(3) * 0.18; - x = x + w; - w = info_pos(3)*0.18; + pos = [x y w h]; + set(handles.contrast_def, 'position', pos); - pos = [x y w h]; - set(handles.contrast_def,'position',pos); + % Brightness text/def + % + x = info_pos(1) + info_pos(3) * 0.5; + w = info_pos(3) * 0.295; - % Brightness text/def - % - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.295; + pos = [x y w h]; + set(handles.Tbrightness, 'position', pos); - pos = [x y w h]; - set(handles.Tbrightness,'position',pos); + x = x + w; + w = info_pos(3) * 0.18; - x = x + w; - w = info_pos(3)*0.18; + pos = [x y w h]; + set(handles.brightness_def, 'position', pos); - pos = [x y w h]; - set(handles.brightness_def,'position',pos); + return % update_usestretch - return; % update_usestretch + % ---------------------------------------------------------------- +function update_useinterp(fig, useinterp) + if isempty(useinterp) + return + end -%---------------------------------------------------------------- -function update_useinterp(fig, useinterp) + nii_menu = getappdata(fig, 'nii_menu'); - if isempty(useinterp) - return; - end + if ~isempty(nii_menu) + if get(nii_menu.Minterp, 'user') + set(nii_menu.Minterp, 'Userdata', 0, 'Label', 'Interp off'); + else + set(nii_menu.Minterp, 'Userdata', 1, 'Label', 'Interp on'); + end + end - nii_menu = getappdata(fig, 'nii_menu'); + nii_view = getappdata(fig, 'nii_view'); + nii_view.useinterp = useinterp; - if ~isempty(nii_menu) - if get(nii_menu.Minterp,'user') - set(nii_menu.Minterp,'Userdata',0,'Label','Interp off'); - else - set(nii_menu.Minterp,'Userdata',1,'Label','Interp on'); - end - end + if ~isempty(nii_view.handles.axial_image) + if strcmpi(get(nii_view.handles.axial_image, 'cdatamapping'), 'direct') + useimagesc = 0; + else + useimagesc = 1; + end + elseif ~isempty(nii_view.handles.coronal_image) + if strcmpi(get(nii_view.handles.coronal_image, 'cdatamapping'), 'direct') + useimagesc = 0; + else + useimagesc = 1; + end + else + if strcmpi(get(nii_view.handles.sagittal_image, 'cdatamapping'), 'direct') + useimagesc = 0; + else + useimagesc = 1; + end + end - nii_view = getappdata(fig, 'nii_view'); - nii_view.useinterp = useinterp; + if ~isempty(nii_view.handles.axial_image) + img_slice = get(nii_view.handles.axial_image, 'cdata'); + delete(nii_view.handles.axial_image); + axes(nii_view.handles.axial_axes); + clim = get(gca, 'clim'); - if ~isempty(nii_view.handles.axial_image) - if strcmpi(get(nii_view.handles.axial_image,'cdatamapping'), 'direct') - useimagesc = 0; - else - useimagesc = 1; - end - elseif ~isempty(nii_view.handles.coronal_image) - if strcmpi(get(nii_view.handles.coronal_image,'cdatamapping'), 'direct') - useimagesc = 0; - else - useimagesc = 1; - end - else - if strcmpi(get(nii_view.handles.sagittal_image,'cdatamapping'), 'direct') - useimagesc = 0; + if useinterp + if useimagesc + nii_view.handles.axial_image = surface(zeros(size(img_slice)), double(img_slice), 'edgecolor', 'none', 'facecolor', 'interp'); else - useimagesc = 1; + nii_view.handles.axial_image = surface(zeros(size(img_slice)), double(img_slice), 'cdatamapping', 'direct', 'edgecolor', 'none', 'facecolor', 'interp'); end - end - - if ~isempty(nii_view.handles.axial_image) - img_slice = get(nii_view.handles.axial_image, 'cdata'); - delete(nii_view.handles.axial_image); - axes(nii_view.handles.axial_axes); - clim = get(gca,'clim'); - - if useinterp - if useimagesc - nii_view.handles.axial_image = surface(zeros(size(img_slice)),double(img_slice),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.axial_image = surface(zeros(size(img_slice)),double(img_slice),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end + else + if useimagesc + nii_view.handles.axial_image = imagesc('cdata', img_slice); else - if useimagesc - nii_view.handles.axial_image = imagesc('cdata',img_slice); - else - nii_view.handles.axial_image = image('cdata',img_slice); - end + nii_view.handles.axial_image = image('cdata', img_slice); end + end - set(gca,'clim',clim); + set(gca, 'clim', clim); - order = get(gca,'child'); - order(find(order == nii_view.handles.axial_image)) = []; - order = [order; nii_view.handles.axial_image]; + order = get(gca, 'child'); + order(find(order == nii_view.handles.axial_image)) = []; + order = [order; nii_view.handles.axial_image]; - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) - order(find(order == nii_view.handles.axial_bg)) = []; - order = [order; nii_view.handles.axial_bg]; - end + if isfield(nii_view.handles, 'axial_bg') & ~isempty(nii_view.handles.axial_bg) + order(find(order == nii_view.handles.axial_bg)) = []; + order = [order; nii_view.handles.axial_bg]; + end - set(gca, 'child', order); + set(gca, 'child', order); - if ~useinterp - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) - delete(nii_view.handles.axial_bg); - nii_view.handles.axial_bg = []; - end + if ~useinterp + if isfield(nii_view.handles, 'axial_bg') & ~isempty(nii_view.handles.axial_bg) + delete(nii_view.handles.axial_bg); + nii_view.handles.axial_bg = []; end + end - set(nii_view.handles.axial_image,'buttondown','view_nii(''axial_image'');'); - end + set(nii_view.handles.axial_image, 'buttondown', 'view_nii(''axial_image'');'); + end - if ~isempty(nii_view.handles.coronal_image) - img_slice = get(nii_view.handles.coronal_image, 'cdata'); - delete(nii_view.handles.coronal_image); - axes(nii_view.handles.coronal_axes); - clim = get(gca,'clim'); + if ~isempty(nii_view.handles.coronal_image) + img_slice = get(nii_view.handles.coronal_image, 'cdata'); + delete(nii_view.handles.coronal_image); + axes(nii_view.handles.coronal_axes); + clim = get(gca, 'clim'); - if useinterp - if useimagesc - nii_view.handles.coronal_image = surface(zeros(size(img_slice)),double(img_slice),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.coronal_image = surface(zeros(size(img_slice)),double(img_slice),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end + if useinterp + if useimagesc + nii_view.handles.coronal_image = surface(zeros(size(img_slice)), double(img_slice), 'edgecolor', 'none', 'facecolor', 'interp'); else - if useimagesc - nii_view.handles.coronal_image = imagesc('cdata',img_slice); - else - nii_view.handles.coronal_image = image('cdata',img_slice); - end + nii_view.handles.coronal_image = surface(zeros(size(img_slice)), double(img_slice), 'cdatamapping', 'direct', 'edgecolor', 'none', 'facecolor', 'interp'); end + else + if useimagesc + nii_view.handles.coronal_image = imagesc('cdata', img_slice); + else + nii_view.handles.coronal_image = image('cdata', img_slice); + end + end - set(gca,'clim',clim); + set(gca, 'clim', clim); - order = get(gca,'child'); - order(find(order == nii_view.handles.coronal_image)) = []; - order = [order; nii_view.handles.coronal_image]; + order = get(gca, 'child'); + order(find(order == nii_view.handles.coronal_image)) = []; + order = [order; nii_view.handles.coronal_image]; - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) - order(find(order == nii_view.handles.coronal_bg)) = []; - order = [order; nii_view.handles.coronal_bg]; - end + if isfield(nii_view.handles, 'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) + order(find(order == nii_view.handles.coronal_bg)) = []; + order = [order; nii_view.handles.coronal_bg]; + end - set(gca, 'child', order); + set(gca, 'child', order); - if ~useinterp - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) - delete(nii_view.handles.coronal_bg); - nii_view.handles.coronal_bg = []; - end + if ~useinterp + if isfield(nii_view.handles, 'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) + delete(nii_view.handles.coronal_bg); + nii_view.handles.coronal_bg = []; end + end - set(nii_view.handles.coronal_image,'buttondown','view_nii(''coronal_image'');'); - end + set(nii_view.handles.coronal_image, 'buttondown', 'view_nii(''coronal_image'');'); + end - if ~isempty(nii_view.handles.sagittal_image) - img_slice = get(nii_view.handles.sagittal_image, 'cdata'); - delete(nii_view.handles.sagittal_image); - axes(nii_view.handles.sagittal_axes); - clim = get(gca,'clim'); + if ~isempty(nii_view.handles.sagittal_image) + img_slice = get(nii_view.handles.sagittal_image, 'cdata'); + delete(nii_view.handles.sagittal_image); + axes(nii_view.handles.sagittal_axes); + clim = get(gca, 'clim'); - if useinterp - if useimagesc - nii_view.handles.sagittal_image = surface(zeros(size(img_slice)),double(img_slice),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.sagittal_image = surface(zeros(size(img_slice)),double(img_slice),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end + if useinterp + if useimagesc + nii_view.handles.sagittal_image = surface(zeros(size(img_slice)), double(img_slice), 'edgecolor', 'none', 'facecolor', 'interp'); + else + nii_view.handles.sagittal_image = surface(zeros(size(img_slice)), double(img_slice), 'cdatamapping', 'direct', 'edgecolor', 'none', 'facecolor', 'interp'); + end + else + if useimagesc + nii_view.handles.sagittal_image = imagesc('cdata', img_slice); else - if useimagesc - nii_view.handles.sagittal_image = imagesc('cdata',img_slice); - else - nii_view.handles.sagittal_image = image('cdata',img_slice); - end + nii_view.handles.sagittal_image = image('cdata', img_slice); end + end - set(gca,'clim',clim); + set(gca, 'clim', clim); - order = get(gca,'child'); - order(find(order == nii_view.handles.sagittal_image)) = []; - order = [order; nii_view.handles.sagittal_image]; + order = get(gca, 'child'); + order(find(order == nii_view.handles.sagittal_image)) = []; + order = [order; nii_view.handles.sagittal_image]; - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) - order(find(order == nii_view.handles.sagittal_bg)) = []; - order = [order; nii_view.handles.sagittal_bg]; - end + if isfield(nii_view.handles, 'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) + order(find(order == nii_view.handles.sagittal_bg)) = []; + order = [order; nii_view.handles.sagittal_bg]; + end - set(gca, 'child', order); + set(gca, 'child', order); - if ~useinterp - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) - delete(nii_view.handles.sagittal_bg); - nii_view.handles.sagittal_bg = []; - end + if ~useinterp + if isfield(nii_view.handles, 'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) + delete(nii_view.handles.sagittal_bg); + nii_view.handles.sagittal_bg = []; end + end - set(nii_view.handles.sagittal_image,'buttondown','view_nii(''sagittal_image'');'); - end - - if ~useinterp - nii_view.bgimg = []; - end + set(nii_view.handles.sagittal_image, 'buttondown', 'view_nii(''sagittal_image'');'); + end - set_coordinates(nii_view,useinterp); - setappdata(fig, 'nii_view', nii_view); + if ~useinterp + nii_view.bgimg = []; + end - return; % update_useinterp + set_coordinates(nii_view, useinterp); + setappdata(fig, 'nii_view', nii_view); + return % update_useinterp -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_useimagesc(fig, useimagesc) - if isempty(useimagesc) - return; - end - - if useimagesc - v='scaled'; - else - v='direct'; - end + if isempty(useimagesc) + return + end - nii_view = getappdata(fig,'nii_view'); - handles = nii_view.handles; + if useimagesc + v = 'scaled'; + else + v = 'direct'; + end - if isfield(handles,'cbar_image') & ishandle(handles.cbar_image) -% set(handles.cbar_image,'cdatamapping',v); - end + nii_view = getappdata(fig, 'nii_view'); + handles = nii_view.handles; - set(handles.axial_image,'cdatamapping',v); - set(handles.coronal_image,'cdatamapping',v); - set(handles.sagittal_image,'cdatamapping',v); + if isfield(handles, 'cbar_image') & ishandle(handles.cbar_image) + % set(handles.cbar_image,'cdatamapping',v); + end - return; % update_useimagesc + set(handles.axial_image, 'cdatamapping', v); + set(handles.coronal_image, 'cdatamapping', v); + set(handles.sagittal_image, 'cdatamapping', v); + return % update_useimagesc -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_shape(fig, area, usecolorbar, usestretch, useimagesc) - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); - if isempty(usestretch) % no change, get usestretch - stretchchange = 0; - usestretch = nii_view.usestretch; - else % change, set usestretch - stretchchange = 1; - nii_view.usestretch = usestretch; - end + if isempty(usestretch) % no change, get usestretch + stretchchange = 0; + usestretch = nii_view.usestretch; + else % change, set usestretch + stretchchange = 1; + nii_view.usestretch = usestretch; + end - if isempty(area) % no change, get area + if isempty(area) % no change, get area - areachange = 0; - area = nii_view.area; + areachange = 0; + area = nii_view.area; - elseif ~isempty(nii_view.cbar_area) % change, set area & cbar_area + elseif ~isempty(nii_view.cbar_area) % change, set area & cbar_area - areachange = 1; - cbar_area = area; - cbar_area(1) = area(1) + area(3)*0.93; - cbar_area(3) = area(3)*0.04; - area(3) = area(3)*0.9; % 90% used for main axes + areachange = 1; + cbar_area = area; + cbar_area(1) = area(1) + area(3) * 0.93; + cbar_area(3) = area(3) * 0.04; + area(3) = area(3) * 0.9; % 90% used for main axes - [cbar_axes cbarminmax_axes] = create_cbar_axes(fig, cbar_area); + [cbar_axes cbarminmax_axes] = create_cbar_axes(fig, cbar_area); - nii_view.area = area; - nii_view.cbar_area = cbar_area; + nii_view.area = area; + nii_view.cbar_area = cbar_area; - else % change, set area only - areachange = 1; - nii_view.area = area; - end + else % change, set area only + areachange = 1; + nii_view.area = area; + end - % Add colorbar - % - if ~isempty(usecolorbar) & usecolorbar & isempty(nii_view.cbar_area) + % Add colorbar + % + if ~isempty(usecolorbar) & usecolorbar & isempty(nii_view.cbar_area) - colorbarchange = 1; + colorbarchange = 1; - cbar_area = area; - cbar_area(1) = area(1) + area(3)*0.93; - cbar_area(3) = area(3)*0.04; - area(3) = area(3)*0.9; % 90% used for main axes + cbar_area = area; + cbar_area(1) = area(1) + area(3) * 0.93; + cbar_area(3) = area(3) * 0.04; + area(3) = area(3) * 0.9; % 90% used for main axes - % create axes for colorbar - % - [cbar_axes cbarminmax_axes] = create_cbar_axes(fig, cbar_area); + % create axes for colorbar + % + [cbar_axes cbarminmax_axes] = create_cbar_axes(fig, cbar_area); - nii_view.area = area; - nii_view.cbar_area = cbar_area; + nii_view.area = area; + nii_view.cbar_area = cbar_area; - % useimagesc follows axial image - % - if isempty(useimagesc) - if strcmpi(get(nii_view.handles.axial_image,'cdatamap'),'scaled') - useimagesc = 1; - else - useimagesc = 0; - end - end - - if isfield(nii_view, 'highcolor') & ~isempty(highcolor) - num_highcolor = size(nii_view.highcolor,1); + % useimagesc follows axial image + % + if isempty(useimagesc) + if strcmpi(get(nii_view.handles.axial_image, 'cdatamap'), 'scaled') + useimagesc = 1; else - num_highcolor = 0; + useimagesc = 0; end + end - if isfield(nii_view, 'colorlevel') & ~isempty(nii_view.colorlevel) - colorlevel = nii_view.colorlevel; - else - colorlevel = 256 - num_highcolor; - end + if isfield(nii_view, 'highcolor') & ~isempty(highcolor) + num_highcolor = size(nii_view.highcolor, 1); + else + num_highcolor = 0; + end - if isfield(nii_view, 'color_map') - color_map = nii_view.color_map; - else - color_map = []; - end + if isfield(nii_view, 'colorlevel') & ~isempty(nii_view.colorlevel) + colorlevel = nii_view.colorlevel; + else + colorlevel = 256 - num_highcolor; + end - if isfield(nii_view, 'highcolor') - highcolor = nii_view.highcolor; - else - highcolor = []; - end + if isfield(nii_view, 'color_map') + color_map = nii_view.color_map; + else + color_map = []; + end - % plot colorbar - % -if 0 - if isempty(color_map) - level = colorlevel + num_highcolor; - else - level = size([color_map; highcolor], 1); - end -end + if isfield(nii_view, 'highcolor') + highcolor = nii_view.highcolor; + else + highcolor = []; + end + % plot colorbar + % + if 0 if isempty(color_map) - level = colorlevel; + level = colorlevel + num_highcolor; else - level = size([color_map], 1); + level = size([color_map; highcolor], 1); end + end - cbar_image = [1:level]'; + if isempty(color_map) + level = colorlevel; + else + level = size([color_map], 1); + end - niiclass = class(nii_view.nii.img); + cbar_image = [1:level]'; - h1 = plot_cbar(fig, cbar_axes, cbarminmax_axes, nii_view.cbarminmax, ... - level, nii_view.handles, useimagesc, nii_view.colorindex, ... - color_map, colorlevel, highcolor, niiclass, nii_view.numscan); - nii_view.handles.cbar_image = h1; - nii_view.handles.cbar_axes = cbar_axes; - nii_view.handles.cbarminmax_axes = cbar_axes; + niiclass = class(nii_view.nii.img); - % remove colorbar - % - elseif ~isempty(usecolorbar) & ~usecolorbar & ~isempty(nii_view.cbar_area) + h1 = plot_cbar(fig, cbar_axes, cbarminmax_axes, nii_view.cbarminmax, ... + level, nii_view.handles, useimagesc, nii_view.colorindex, ... + color_map, colorlevel, highcolor, niiclass, nii_view.numscan); + nii_view.handles.cbar_image = h1; + nii_view.handles.cbar_axes = cbar_axes; + nii_view.handles.cbarminmax_axes = cbar_axes; - colorbarchange = 1; + % remove colorbar + % + elseif ~isempty(usecolorbar) & ~usecolorbar & ~isempty(nii_view.cbar_area) - area(3) = area(3) / 0.9; + colorbarchange = 1; - nii_view.area = area; - nii_view.cbar_area = []; + area(3) = area(3) / 0.9; - nii_view.handles = rmfield(nii_view.handles,'cbar_image'); - delete(nii_view.handles.cbarminmax_axes); - nii_view.handles = rmfield(nii_view.handles,'cbarminmax_axes'); - delete(nii_view.handles.cbar_axes); - nii_view.handles = rmfield(nii_view.handles,'cbar_axes'); + nii_view.area = area; + nii_view.cbar_area = []; - else - colorbarchange = 0; - end + nii_view.handles = rmfield(nii_view.handles, 'cbar_image'); + delete(nii_view.handles.cbarminmax_axes); + nii_view.handles = rmfield(nii_view.handles, 'cbarminmax_axes'); + delete(nii_view.handles.cbar_axes); + nii_view.handles = rmfield(nii_view.handles, 'cbar_axes'); - if colorbarchange | stretchchange | areachange - setappdata(fig,'nii_view',nii_view); - update_usestretch(fig, usestretch); - end + else + colorbarchange = 0; + end - return; % update_shape + if colorbarchange | stretchchange | areachange + setappdata(fig, 'nii_view', nii_view); + update_usestretch(fig, usestretch); + end + return % update_shape -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_unit(fig, setunit) - if isempty(setunit) - return; - end - - if strcmpi(setunit,'mm') | strcmpi(setunit,'millimeter') | strcmpi(setunit,'mni') - v = 2; -% elseif strcmpi(setunit,'tal') | strcmpi(setunit,'talairach') - % v = 3; - elseif strcmpi(setunit,'vox') | strcmpi(setunit,'voxel') - v = 1; - else - v = 1; - end + if isempty(setunit) + return + end - nii_view = getappdata(fig,'nii_view'); - set(nii_view.handles.coord, 'value', v); - set_image_value(nii_view); + if strcmpi(setunit, 'mm') | strcmpi(setunit, 'millimeter') | strcmpi(setunit, 'mni') + v = 2; + % elseif strcmpi(setunit,'tal') | strcmpi(setunit,'talairach') + % v = 3; + elseif strcmpi(setunit, 'vox') | strcmpi(setunit, 'voxel') + v = 1; + else + v = 1; + end - return; % update_unit + nii_view = getappdata(fig, 'nii_view'); + set(nii_view.handles.coord, 'value', v); + set_image_value(nii_view); + return % update_unit -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_viewpoint(fig, setviewpoint) - if isempty(setviewpoint) - return; - end + if isempty(setviewpoint) + return + end - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); - if length(setviewpoint) ~= 3 - error('Viewpoint position should contain [x y z]'); - end + if length(setviewpoint) ~= 3 + error('Viewpoint position should contain [x y z]'); + end - set(nii_view.handles.impos,'string',num2str(setviewpoint)); + set(nii_view.handles.impos, 'string', num2str(setviewpoint)); - opt.command = 'impos_edit'; - view_nii(fig, opt); + opt.command = 'impos_edit'; + view_nii(fig, opt); - set(nii_view.handles.axial_axes,'selected','on'); - set(nii_view.handles.axial_axes,'selected','off'); - set(nii_view.handles.coronal_axes,'selected','on'); - set(nii_view.handles.coronal_axes,'selected','off'); - set(nii_view.handles.sagittal_axes,'selected','on'); - set(nii_view.handles.sagittal_axes,'selected','off'); + set(nii_view.handles.axial_axes, 'selected', 'on'); + set(nii_view.handles.axial_axes, 'selected', 'off'); + set(nii_view.handles.coronal_axes, 'selected', 'on'); + set(nii_view.handles.coronal_axes, 'selected', 'off'); + set(nii_view.handles.sagittal_axes, 'selected', 'on'); + set(nii_view.handles.sagittal_axes, 'selected', 'off'); - return; % update_viewpoint + return % update_viewpoint - -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_scanid(fig, setscanid) - if isempty(setscanid) - return; - end - - nii_view = getappdata(fig,'nii_view'); + if isempty(setscanid) + return + end - if setscanid < 1 - setscanid = 1; - end + nii_view = getappdata(fig, 'nii_view'); - if setscanid > nii_view.numscan - setscanid = nii_view.numscan; - end + if setscanid < 1 + setscanid = 1; + end - set(nii_view.handles.contrast_def,'string',num2str(setscanid)); - set(nii_view.handles.contrast,'value',setscanid); + if setscanid > nii_view.numscan + setscanid = nii_view.numscan; + end - opt.command = 'updateimg'; - opt.setscanid = setscanid; + set(nii_view.handles.contrast_def, 'string', num2str(setscanid)); + set(nii_view.handles.contrast, 'value', setscanid); - view_nii(fig, nii_view.nii.img, opt); + opt.command = 'updateimg'; + opt.setscanid = setscanid; - return; % update_scanid + view_nii(fig, nii_view.nii.img, opt); + return % update_scanid -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_crosshaircolor(fig, new_color) - if isempty(new_color) - return; - end - - nii_view = getappdata(fig,'nii_view'); - xhair_color = nii_view.handles.xhair_color; + if isempty(new_color) + return + end - set(xhair_color,'user',new_color); - set(nii_view.axi_xhair.lx,'color',new_color); - set(nii_view.axi_xhair.ly,'color',new_color); - set(nii_view.cor_xhair.lx,'color',new_color); - set(nii_view.cor_xhair.ly,'color',new_color); - set(nii_view.sag_xhair.lx,'color',new_color); - set(nii_view.sag_xhair.ly,'color',new_color); + nii_view = getappdata(fig, 'nii_view'); + xhair_color = nii_view.handles.xhair_color; - return; % update_crosshaircolor + set(xhair_color, 'user', new_color); + set(nii_view.axi_xhair.lx, 'color', new_color); + set(nii_view.axi_xhair.ly, 'color', new_color); + set(nii_view.cor_xhair.lx, 'color', new_color); + set(nii_view.cor_xhair.ly, 'color', new_color); + set(nii_view.sag_xhair.lx, 'color', new_color); + set(nii_view.sag_xhair.ly, 'color', new_color); + return % update_crosshaircolor -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_colorindex(fig, colorindex) - if isempty(colorindex) - return; - end - - nii_view = getappdata(fig,'nii_view'); - nii_view.colorindex = colorindex; - setappdata(fig, 'nii_view', nii_view); - set(nii_view.handles.colorindex,'value',colorindex); + if isempty(colorindex) + return + end - opt.command = 'color'; - view_nii(fig, opt); + nii_view = getappdata(fig, 'nii_view'); + nii_view.colorindex = colorindex; + setappdata(fig, 'nii_view', nii_view); + set(nii_view.handles.colorindex, 'value', colorindex); - return; % update_colorindex + opt.command = 'color'; + view_nii(fig, opt); + return % update_colorindex -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function redraw_cbar(fig, colorlevel, color_map, highcolor) - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); - if isempty(nii_view.cbar_area) - return; - end + if isempty(nii_view.cbar_area) + return + end - colorindex = nii_view.colorindex; + colorindex = nii_view.colorindex; - if isempty(highcolor) - num_highcolor = 0; - else - num_highcolor = size(highcolor,1); - end - - if isempty(colorlevel) - colorlevel=256; - end + if isempty(highcolor) + num_highcolor = 0; + else + num_highcolor = size(highcolor, 1); + end - if colorindex == 1 - colorlevel = size(color_map, 1); - end + if isempty(colorlevel) + colorlevel = 256; + end -% level = colorlevel + num_highcolor; - level = colorlevel; + if colorindex == 1 + colorlevel = size(color_map, 1); + end - cbar_image = [1:level]'; + % level = colorlevel + num_highcolor; + level = colorlevel; - cbar_area = nii_view.cbar_area; + cbar_image = [1:level]'; - % useimagesc follows axial image - % - if strcmpi(get(nii_view.handles.axial_image,'cdatamap'),'scaled') - useimagesc = 1; - else - useimagesc = 0; - end + cbar_area = nii_view.cbar_area; - niiclass = class(nii_view.nii.img); + % useimagesc follows axial image + % + if strcmpi(get(nii_view.handles.axial_image, 'cdatamap'), 'scaled') + useimagesc = 1; + else + useimagesc = 0; + end - delete(nii_view.handles.cbar_image); - delete(nii_view.handles.cbar_axes); - delete(nii_view.handles.cbarminmax_axes); + niiclass = class(nii_view.nii.img); - [nii_view.handles.cbar_axes nii_view.handles.cbarminmax_axes] = ... - create_cbar_axes(fig, cbar_area, []); + delete(nii_view.handles.cbar_image); + delete(nii_view.handles.cbar_axes); + delete(nii_view.handles.cbarminmax_axes); - nii_view.handles.cbar_image = plot_cbar(fig, ... - nii_view.handles.cbar_axes, nii_view.handles.cbarminmax_axes, ... - nii_view.cbarminmax, level, nii_view.handles, useimagesc, ... - colorindex, color_map, colorlevel, highcolor, niiclass, ... - nii_view.numscan, []); + [nii_view.handles.cbar_axes nii_view.handles.cbarminmax_axes] = ... + create_cbar_axes(fig, cbar_area, []); - setappdata(fig, 'nii_view', nii_view); + nii_view.handles.cbar_image = plot_cbar(fig, ... + nii_view.handles.cbar_axes, nii_view.handles.cbarminmax_axes, ... + nii_view.cbarminmax, level, nii_view.handles, useimagesc, ... + colorindex, color_map, colorlevel, highcolor, niiclass, ... + nii_view.numscan, []); - return; % redraw_cbar + setappdata(fig, 'nii_view', nii_view); + return % redraw_cbar -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_buttondown(fig, setbuttondown) - if isempty(setbuttondown) - return; - end - - nii_view = getappdata(fig,'nii_view'); - nii_view.buttondown = setbuttondown; - setappdata(fig, 'nii_view', nii_view); + if isempty(setbuttondown) + return + end - return; % update_buttondown + nii_view = getappdata(fig, 'nii_view'); + nii_view.buttondown = setbuttondown; + setappdata(fig, 'nii_view', nii_view); + return % update_buttondown -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_cbarminmax(fig, cbarminmax) - if isempty(cbarminmax) - return; - end - - nii_view = getappdata(fig, 'nii_view'); + if isempty(cbarminmax) + return + end - if ~isfield(nii_view.handles, 'cbarminmax_axes') - return; - end + nii_view = getappdata(fig, 'nii_view'); - nii_view.cbarminmax = cbarminmax; - setappdata(fig, 'nii_view', nii_view); + if ~isfield(nii_view.handles, 'cbarminmax_axes') + return + end - axes(nii_view.handles.cbarminmax_axes); + nii_view.cbarminmax = cbarminmax; + setappdata(fig, 'nii_view', nii_view); - plot([0 0], cbarminmax, 'w'); - axis tight; + axes(nii_view.handles.cbarminmax_axes); - set(nii_view.handles.cbarminmax_axes,'YDir','normal', ... - 'XLimMode','manual','YLimMode','manual','YColor',[0 0 0], ... - 'XColor',[0 0 0],'xtick',[],'YAxisLocation','right'); + plot([0 0], cbarminmax, 'w'); + axis tight; - ylim = get(nii_view.handles.cbar_axes,'ylim'); - ylimb = get(nii_view.handles.cbarminmax_axes,'ylim'); - ytickb = get(nii_view.handles.cbarminmax_axes,'ytick'); - ytick=(ylim(2)-ylim(1))*(ytickb-ylimb(1))/(ylimb(2)-ylimb(1))+ylim(1); + set(nii_view.handles.cbarminmax_axes, 'YDir', 'normal', ... + 'XLimMode', 'manual', 'YLimMode', 'manual', 'YColor', [0 0 0], ... + 'XColor', [0 0 0], 'xtick', [], 'YAxisLocation', 'right'); - axes(nii_view.handles.cbar_axes); + ylim = get(nii_view.handles.cbar_axes, 'ylim'); + ylimb = get(nii_view.handles.cbarminmax_axes, 'ylim'); + ytickb = get(nii_view.handles.cbarminmax_axes, 'ytick'); + ytick = (ylim(2) - ylim(1)) * (ytickb - ylimb(1)) / (ylimb(2) - ylimb(1)) + ylim(1); - set(nii_view.handles.cbar_axes,'YDir','normal','XLimMode','manual', ... - 'YLimMode','manual','YColor',[0 0 0],'XColor',[0 0 0],'xtick',[], ... - 'YAxisLocation','right','ylim',ylim,'ytick',ytick,'yticklabel',''); + axes(nii_view.handles.cbar_axes); - return; % update_cbarminmax + set(nii_view.handles.cbar_axes, 'YDir', 'normal', 'XLimMode', 'manual', ... + 'YLimMode', 'manual', 'YColor', [0 0 0], 'XColor', [0 0 0], 'xtick', [], ... + 'YAxisLocation', 'right', 'ylim', ylim, 'ytick', ytick, 'yticklabel', ''); + return % update_cbarminmax -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_highcolor(fig, highcolor, colorlevel) - nii_view = getappdata(fig,'nii_view'); + nii_view = getappdata(fig, 'nii_view'); - if ischar(highcolor) & (isempty(colorlevel) | nii_view.colorindex == 1) - return; - end + if ischar(highcolor) & (isempty(colorlevel) | nii_view.colorindex == 1) + return + end - if ~ischar(highcolor) - nii_view.highcolor = highcolor; + if ~ischar(highcolor) + nii_view.highcolor = highcolor; - if isempty(highcolor) - nii_view = rmfield(nii_view, 'highcolor'); - end - else - highcolor = []; - end - - if isempty(colorlevel) | nii_view.colorindex == 1 - nii_view.colorlevel = nii_view.colorlevel - size(highcolor,1); - else - nii_view.colorlevel = colorlevel; - end + if isempty(highcolor) + nii_view = rmfield(nii_view, 'highcolor'); + end + else + highcolor = []; + end - setappdata(fig, 'nii_view', nii_view); + if isempty(colorlevel) | nii_view.colorindex == 1 + nii_view.colorlevel = nii_view.colorlevel - size(highcolor, 1); + else + nii_view.colorlevel = colorlevel; + end - if isfield(nii_view,'color_map') - color_map = nii_view.color_map; - else - color_map = []; - end + setappdata(fig, 'nii_view', nii_view); - redraw_cbar(fig, nii_view.colorlevel, color_map, highcolor); - change_colormap(fig); + if isfield(nii_view, 'color_map') + color_map = nii_view.color_map; + else + color_map = []; + end - return; % update_highcolor + redraw_cbar(fig, nii_view.colorlevel, color_map, highcolor); + change_colormap(fig); + return % update_highcolor -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function update_colormap(fig, color_map) - if ischar(color_map) - return; - end - - nii_view = getappdata(fig,'nii_view'); - nii = nii_view.nii; - minvalue = nii_view.minvalue; - - if isempty(color_map) - if minvalue < 0 - colorindex = 2; - else - colorindex = 3; - end - - nii_view = rmfield(nii_view, 'color_map'); - setappdata(fig,'nii_view',nii_view); - update_colorindex(fig, colorindex); - return; - else - colorindex = 1; - nii_view.color_map = color_map; - nii_view.colorindex = colorindex; - setappdata(fig,'nii_view',nii_view); - set(nii_view.handles.colorindex,'value',colorindex); - end - - colorlevel = nii_view.colorlevel; - - if isfield(nii_view, 'highcolor') - highcolor = nii_view.highcolor; - else - highcolor = []; - end - - redraw_cbar(fig, colorlevel, color_map, highcolor); - change_colormap(fig); - - opt.enablecontrast = 0; - update_enable(fig, opt); - - return; % update_colormap - - -%---------------------------------------------------------------- -function status = get_status(h); - - nii_view = getappdata(h,'nii_view'); - - status.fig = h; - status.area = nii_view.area; - - if isempty(nii_view.cbar_area) - status.usecolorbar = 0; - else - status.usecolorbar = 1; - width = status.area(3) / 0.9; - status.area(3) = width; - end - - if strcmpi(get(nii_view.handles.imval,'visible'), 'on') - status.usepanel = 1; - else - status.usepanel = 0; - end - - if get(nii_view.handles.xhair,'value') == 1 - status.usecrosshair = 1; - else - status.usecrosshair = 0; - end - - status.usestretch = nii_view.usestretch; - - if strcmpi(get(nii_view.handles.axial_image,'cdatamapping'), 'direct') - status.useimagesc = 0; - else - status.useimagesc = 1; - end - - status.useinterp = nii_view.useinterp; - - if get(nii_view.handles.coord,'value') == 1 - status.unit = 'vox'; - elseif get(nii_view.handles.coord,'value') == 2 - status.unit = 'mm'; - elseif get(nii_view.handles.coord,'value') == 3 - status.unit = 'tal'; - end - - status.viewpoint = get(nii_view.handles.impos,'value'); - status.scanid = nii_view.scanid; - status.intensity = get(nii_view.handles.imval,'value'); - status.colorindex = get(nii_view.handles.colorindex,'value'); + if ischar(color_map) + return + end - if isfield(nii_view,'color_map') - status.colormap = nii_view.color_map; - else - status.colormap = []; - end - - status.colorlevel = nii_view.colorlevel; - - if isfield(nii_view,'highcolor') - status.highcolor = nii_view.highcolor; - else - status.highcolor = []; - end - - status.cbarminmax = nii_view.cbarminmax; - status.buttondown = nii_view.buttondown; - - return; % get_status + nii_view = getappdata(fig, 'nii_view'); + nii = nii_view.nii; + minvalue = nii_view.minvalue; + if isempty(color_map) + if minvalue < 0 + colorindex = 2; + else + colorindex = 3; + end -%---------------------------------------------------------------- + nii_view = rmfield(nii_view, 'color_map'); + setappdata(fig, 'nii_view', nii_view); + update_colorindex(fig, colorindex); + return + else + colorindex = 1; + nii_view.color_map = color_map; + nii_view.colorindex = colorindex; + setappdata(fig, 'nii_view', nii_view); + set(nii_view.handles.colorindex, 'value', colorindex); + end + + colorlevel = nii_view.colorlevel; + + if isfield(nii_view, 'highcolor') + highcolor = nii_view.highcolor; + else + highcolor = []; + end + + redraw_cbar(fig, colorlevel, color_map, highcolor); + change_colormap(fig); + + opt.enablecontrast = 0; + update_enable(fig, opt); + + return % update_colormap + + % ---------------------------------------------------------------- +function status = get_status(h) + + nii_view = getappdata(h, 'nii_view'); + + status.fig = h; + status.area = nii_view.area; + + if isempty(nii_view.cbar_area) + status.usecolorbar = 0; + else + status.usecolorbar = 1; + width = status.area(3) / 0.9; + status.area(3) = width; + end + + if strcmpi(get(nii_view.handles.imval, 'visible'), 'on') + status.usepanel = 1; + else + status.usepanel = 0; + end + + if get(nii_view.handles.xhair, 'value') == 1 + status.usecrosshair = 1; + else + status.usecrosshair = 0; + end + + status.usestretch = nii_view.usestretch; + + if strcmpi(get(nii_view.handles.axial_image, 'cdatamapping'), 'direct') + status.useimagesc = 0; + else + status.useimagesc = 1; + end + + status.useinterp = nii_view.useinterp; + + if get(nii_view.handles.coord, 'value') == 1 + status.unit = 'vox'; + elseif get(nii_view.handles.coord, 'value') == 2 + status.unit = 'mm'; + elseif get(nii_view.handles.coord, 'value') == 3 + status.unit = 'tal'; + end + + status.viewpoint = get(nii_view.handles.impos, 'value'); + status.scanid = nii_view.scanid; + status.intensity = get(nii_view.handles.imval, 'value'); + status.colorindex = get(nii_view.handles.colorindex, 'value'); + + if isfield(nii_view, 'color_map') + status.colormap = nii_view.color_map; + else + status.colormap = []; + end + + status.colorlevel = nii_view.colorlevel; + + if isfield(nii_view, 'highcolor') + status.highcolor = nii_view.highcolor; + else + status.highcolor = []; + end + + status.cbarminmax = nii_view.cbarminmax; + status.buttondown = nii_view.buttondown; + + return % get_status + + % ---------------------------------------------------------------- function [custom_color_map, colorindex] ... - = change_colormap(fig, nii, colorindex, cbarminmax) - - custom_color_map = []; - - if ~exist('nii', 'var') - nii_view = getappdata(fig,'nii_view'); - else - nii_view = nii; - end - - if ~exist('colorindex', 'var') - colorindex = get(nii_view.handles.colorindex,'value'); - end - - if ~exist('cbarminmax', 'var') - cbarminmax = nii_view.cbarminmax; - end - - if isfield(nii_view, 'highcolor') & ~isempty(nii_view.highcolor) - highcolor = nii_view.highcolor; - num_highcolor = size(highcolor,1); - else - highcolor = []; - num_highcolor = 0; - end - -% if isfield(nii_view, 'colorlevel') & ~isempty(nii_view.colorlevel) - if nii_view.colorlevel < 256 - num_color = nii_view.colorlevel; - else - num_color = 256 - num_highcolor; - end - - contrast = []; - - if colorindex == 3 % for gray - if nii_view.numscan > 1 - contrast = 1; - else - contrast = (num_color-1)*(get(nii_view.handles.contrast,'value')-1)/255+1; - contrast = floor(contrast); - end - elseif colorindex == 2 % for bipolar - if nii_view.numscan > 1 - contrast = 128; - else - contrast = get(nii_view.handles.contrast,'value'); - end - end + = change_colormap(fig, nii, colorindex, cbarminmax) + + custom_color_map = []; + + if ~exist('nii', 'var') + nii_view = getappdata(fig, 'nii_view'); + else + nii_view = nii; + end + + if ~exist('colorindex', 'var') + colorindex = get(nii_view.handles.colorindex, 'value'); + end + + if ~exist('cbarminmax', 'var') + cbarminmax = nii_view.cbarminmax; + end + + if isfield(nii_view, 'highcolor') & ~isempty(nii_view.highcolor) + highcolor = nii_view.highcolor; + num_highcolor = size(highcolor, 1); + else + highcolor = []; + num_highcolor = 0; + end + + % if isfield(nii_view, 'colorlevel') & ~isempty(nii_view.colorlevel) + if nii_view.colorlevel < 256 + num_color = nii_view.colorlevel; + else + num_color = 256 - num_highcolor; + end + + contrast = []; + + if colorindex == 3 % for gray + if nii_view.numscan > 1 + contrast = 1; + else + contrast = (num_color - 1) * (get(nii_view.handles.contrast, 'value') - 1) / 255 + 1; + contrast = floor(contrast); + end + elseif colorindex == 2 % for bipolar + if nii_view.numscan > 1 + contrast = 128; + else + contrast = get(nii_view.handles.contrast, 'value'); + end + end + + if isfield(nii_view, 'color_map') & ~isempty(nii_view.color_map) + color_map = nii_view.color_map; + custom_color_map = color_map; + elseif colorindex == 1 + [f p] = uigetfile('*.txt', 'Input colormap text file'); + + if p == 0 + colorindex = nii_view.colorindex; + set(nii_view.handles.colorindex, 'value', colorindex); + return + end - if isfield(nii_view,'color_map') & ~isempty(nii_view.color_map) - color_map = nii_view.color_map; - custom_color_map = color_map; - elseif colorindex == 1 - [f p] = uigetfile('*.txt', 'Input colormap text file'); - - if p==0 - colorindex = nii_view.colorindex; - set(nii_view.handles.colorindex,'value',colorindex); - return; - end; - - try - custom_color_map = load(fullfile(p,f)); - loadfail = 0; - catch - loadfail = 1; - end + try + custom_color_map = load(fullfile(p, f)); + loadfail = 0; + catch + loadfail = 1; + end - if loadfail | isempty(custom_color_map) | size(custom_color_map,2)~=3 ... - | min(custom_color_map(:)) < 0 | max(custom_color_map(:)) > 1 + if loadfail | isempty(custom_color_map) | size(custom_color_map, 2) ~= 3 ... + | min(custom_color_map(:)) < 0 | max(custom_color_map(:)) > 1 - msg = 'Colormap should be a Mx3 matrix with value between 0 and 1'; - msgbox(msg,'Error in colormap file'); - colorindex = nii_view.colorindex; - set(nii_view.handles.colorindex,'value',colorindex); - return; - end + msg = 'Colormap should be a Mx3 matrix with value between 0 and 1'; + msgbox(msg, 'Error in colormap file'); + colorindex = nii_view.colorindex; + set(nii_view.handles.colorindex, 'value', colorindex); + return + end - color_map = custom_color_map; - nii_view.color_map = color_map; - end + color_map = custom_color_map; + nii_view.color_map = color_map; + end - switch colorindex - case {2} + switch colorindex + case {2} color_map = bipolar(num_color, cbarminmax(1), cbarminmax(2), contrast); - case {3} + case {3} color_map = gray(num_color - contrast + 1); - case {4} + case {4} color_map = jet(num_color); - case {5} + case {5} color_map = cool(num_color); - case {6} + case {6} color_map = bone(num_color); - case {7} + case {7} color_map = hot(num_color); - case {8} + case {8} color_map = copper(num_color); - case {9} + case {9} color_map = pink(num_color); - end + end - nii_view.colorindex = colorindex; + nii_view.colorindex = colorindex; - if ~exist('nii', 'var') - setappdata(fig,'nii_view',nii_view); - end + if ~exist('nii', 'var') + setappdata(fig, 'nii_view', nii_view); + end - if colorindex == 3 - color_map = [zeros(contrast,3); color_map(2:end,:)]; - end + if colorindex == 3 + color_map = [zeros(contrast, 3); color_map(2:end, :)]; + end - if get(nii_view.handles.neg_color,'value') & isempty(highcolor) - color_map = flipud(color_map); - elseif get(nii_view.handles.neg_color,'value') & ~isempty(highcolor) - highcolor = flipud(highcolor); - end + if get(nii_view.handles.neg_color, 'value') & isempty(highcolor) + color_map = flipud(color_map); + elseif get(nii_view.handles.neg_color, 'value') & ~isempty(highcolor) + highcolor = flipud(highcolor); + end - brightness = get(nii_view.handles.brightness,'value'); - color_map = brighten(color_map, brightness); + brightness = get(nii_view.handles.brightness, 'value'); + color_map = brighten(color_map, brightness); - color_map = [color_map; highcolor]; + color_map = [color_map; highcolor]; - set(fig, 'colormap', color_map); + set(fig, 'colormap', color_map); - return; % change_colormap + return % change_colormap - -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function move_cursor(fig) - nii_view = getappdata(fig, 'nii_view'); + nii_view = getappdata(fig, 'nii_view'); - if isempty(nii_view) - return; - end + if isempty(nii_view) + return + end - axi = get(nii_view.handles.axial_axes, 'pos'); - cor = get(nii_view.handles.coronal_axes, 'pos'); - sag = get(nii_view.handles.sagittal_axes, 'pos'); - curr = get(fig, 'currentpoint'); + axi = get(nii_view.handles.axial_axes, 'pos'); + cor = get(nii_view.handles.coronal_axes, 'pos'); + sag = get(nii_view.handles.sagittal_axes, 'pos'); + curr = get(fig, 'currentpoint'); - if curr(1) >= axi(1) & curr(1) <= axi(1)+axi(3) & ... - curr(2) >= axi(2) & curr(2) <= axi(2)+axi(4) + if curr(1) >= axi(1) & curr(1) <= axi(1) + axi(3) & ... + curr(2) >= axi(2) & curr(2) <= axi(2) + axi(4) - curr = get(nii_view.handles.axial_axes, 'current'); - sag = curr(1,1); - cor = curr(1,2); - axi = nii_view.slices.axi; + curr = get(nii_view.handles.axial_axes, 'current'); + sag = curr(1, 1); + cor = curr(1, 2); + axi = nii_view.slices.axi; - elseif curr(1) >= cor(1) & curr(1) <= cor(1)+cor(3) & ... - curr(2) >= cor(2) & curr(2) <= cor(2)+cor(4) + elseif curr(1) >= cor(1) & curr(1) <= cor(1) + cor(3) & ... + curr(2) >= cor(2) & curr(2) <= cor(2) + cor(4) - curr = get(nii_view.handles.coronal_axes, 'current'); - sag = curr(1,1); - cor = nii_view.slices.cor; - axi = curr(1,2); + curr = get(nii_view.handles.coronal_axes, 'current'); + sag = curr(1, 1); + cor = nii_view.slices.cor; + axi = curr(1, 2); - elseif curr(1) >= sag(1) & curr(1) <= sag(1)+sag(3) & ... - curr(2) >= sag(2) & curr(2) <= sag(2)+sag(4) + elseif curr(1) >= sag(1) & curr(1) <= sag(1) + sag(3) & ... + curr(2) >= sag(2) & curr(2) <= sag(2) + sag(4) - curr = get(nii_view.handles.sagittal_axes, 'current'); + curr = get(nii_view.handles.sagittal_axes, 'current'); - sag = nii_view.slices.sag; - cor = curr(1,1); - axi = curr(1,2); + sag = nii_view.slices.sag; + cor = curr(1, 1); + axi = curr(1, 2); - else + else - set(nii_view.handles.imvalcur,'String',' '); - set(nii_view.handles.imposcur,'String',' '); - return; + set(nii_view.handles.imvalcur, 'String', ' '); + set(nii_view.handles.imposcur, 'String', ' '); + return - end + end - sag = round(sag); - cor = round(cor); - axi = round(axi); + sag = round(sag); + cor = round(cor); + axi = round(axi); - if sag < 1 - sag = 1; - elseif sag > nii_view.dims(1) - sag = nii_view.dims(1); - end + if sag < 1 + sag = 1; + elseif sag > nii_view.dims(1) + sag = nii_view.dims(1); + end - if cor < 1 - cor = 1; - elseif cor > nii_view.dims(2) - cor = nii_view.dims(2); - end + if cor < 1 + cor = 1; + elseif cor > nii_view.dims(2) + cor = nii_view.dims(2); + end - if axi < 1 - axi = 1; - elseif axi > nii_view.dims(3) - axi = nii_view.dims(3); - end + if axi < 1 + axi = 1; + elseif axi > nii_view.dims(3) + axi = nii_view.dims(3); + end - if 0 % isfield(nii_view, 'disp') - img = nii_view.disp; - else - img = nii_view.nii.img; + if 0 % isfield(nii_view, 'disp') + img = nii_view.disp; + else + img = nii_view.nii.img; + end + + if nii_view.nii.hdr.dime.datatype == 128 + imgvalue = [double(img(sag, cor, axi, 1, nii_view.scanid)) double(img(sag, cor, axi, 2, nii_view.scanid)) double(img(sag, cor, axi, 3, nii_view.scanid))]; + set(nii_view.handles.imvalcur, 'String', sprintf('%7.4g %7.4g %7.4g', imgvalue)); + elseif nii_view.nii.hdr.dime.datatype == 511 + R = double(img(sag, cor, axi, 1, nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... + nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; + G = double(img(sag, cor, axi, 2, nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... + nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; + B = double(img(sag, cor, axi, 3, nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... + nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; + imgvalue = [R G B]; + set(nii_view.handles.imvalcur, 'String', sprintf('%7.4g %7.4g %7.4g', imgvalue)); + else + imgvalue = double(img(sag, cor, axi, nii_view.scanid)); + + if isnan(imgvalue) | imgvalue > nii_view.cbarminmax(2) + imgvalue = 0; end - if nii_view.nii.hdr.dime.datatype == 128 - imgvalue = [double(img(sag,cor,axi,1,nii_view.scanid)) double(img(sag,cor,axi,2,nii_view.scanid)) double(img(sag,cor,axi,3,nii_view.scanid))]; - set(nii_view.handles.imvalcur,'String',sprintf('%7.4g %7.4g %7.4g',imgvalue)); - elseif nii_view.nii.hdr.dime.datatype == 511 - R = double(img(sag,cor,axi,1,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - G = double(img(sag,cor,axi,2,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - B = double(img(sag,cor,axi,3,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - imgvalue = [R G B]; - set(nii_view.handles.imvalcur,'String',sprintf('%7.4g %7.4g %7.4g',imgvalue)); - else - imgvalue = double(img(sag,cor,axi,nii_view.scanid)); + set(nii_view.handles.imvalcur, 'String', sprintf('%.6g', imgvalue)); + end - if isnan(imgvalue) | imgvalue > nii_view.cbarminmax(2) - imgvalue = 0; - end + nii_view.slices.sag = sag; + nii_view.slices.cor = cor; + nii_view.slices.axi = axi; - set(nii_view.handles.imvalcur,'String',sprintf('%.6g',imgvalue)); - end + nii_view = update_imgXYZ(nii_view); - nii_view.slices.sag = sag; - nii_view.slices.cor = cor; - nii_view.slices.axi = axi; - - nii_view = update_imgXYZ(nii_view); - - if get(nii_view.handles.coord,'value') == 1, - sag = nii_view.imgXYZ.vox(1); - cor = nii_view.imgXYZ.vox(2); - axi = nii_view.imgXYZ.vox(3); - elseif get(nii_view.handles.coord,'value') == 2, - sag = nii_view.imgXYZ.mm(1); - cor = nii_view.imgXYZ.mm(2); - axi = nii_view.imgXYZ.mm(3); - elseif get(nii_view.handles.coord,'value') == 3, - sag = nii_view.imgXYZ.tal(1); - cor = nii_view.imgXYZ.tal(2); - axi = nii_view.imgXYZ.tal(3); - end + if get(nii_view.handles.coord, 'value') == 1 + sag = nii_view.imgXYZ.vox(1); + cor = nii_view.imgXYZ.vox(2); + axi = nii_view.imgXYZ.vox(3); + elseif get(nii_view.handles.coord, 'value') == 2 + sag = nii_view.imgXYZ.mm(1); + cor = nii_view.imgXYZ.mm(2); + axi = nii_view.imgXYZ.mm(3); + elseif get(nii_view.handles.coord, 'value') == 3 + sag = nii_view.imgXYZ.tal(1); + cor = nii_view.imgXYZ.tal(2); + axi = nii_view.imgXYZ.tal(3); + end - if get(nii_view.handles.coord,'value') == 1, - string = sprintf('%7.0f %7.0f %7.0f',sag,cor,axi); - else - string = sprintf('%7.1f %7.1f %7.1f',sag,cor,axi); - end; - - set(nii_view.handles.imposcur,'String',string); + if get(nii_view.handles.coord, 'value') == 1 + string = sprintf('%7.0f %7.0f %7.0f', sag, cor, axi); + else + string = sprintf('%7.1f %7.1f %7.1f', sag, cor, axi); + end - return; % move_cursor + set(nii_view.handles.imposcur, 'String', string); + return % move_cursor -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function change_scan(hdl_str) - fig = gcbf; - nii_view = getappdata(fig,'nii_view'); - - if strcmpi(hdl_str, 'edit_change_scan') % edit - hdl = nii_view.handles.contrast_def; - setscanid = round(str2num(get(hdl, 'string'))); - else % slider - hdl = nii_view.handles.contrast; - setscanid = round(get(hdl, 'value')); - end + fig = gcbf; + nii_view = getappdata(fig, 'nii_view'); - update_scanid(fig, setscanid); + if strcmpi(hdl_str, 'edit_change_scan') % edit + hdl = nii_view.handles.contrast_def; + setscanid = round(str2num(get(hdl, 'string'))); + else % slider + hdl = nii_view.handles.contrast; + setscanid = round(get(hdl, 'value')); + end - return; % change_scan + update_scanid(fig, setscanid); + return % change_scan -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function val = scale_in(val, minval, maxval, range) - % scale value into range - % - val = range*(double(val)-double(minval))/(double(maxval)-double(minval))+1; + % scale value into range + % + val = range * (double(val) - double(minval)) / (double(maxval) - double(minval)) + 1; - return; % scale_in + return % scale_in - -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function val = scale_out(val, minval, maxval, range) - % according to [minval maxval] and range of color levels (e.g. 199) - % scale val back from any thing between 1~256 to a small number that - % is corresonding to [minval maxval]. - % - val = (double(val)-1)*(double(maxval)-double(minval))/range+double(minval); - - return; % scale_out + % according to [minval maxval] and range of color levels (e.g. 199) + % scale val back from any thing between 1~256 to a small number that + % is corresonding to [minval maxval]. + % + val = (double(val) - 1) * (double(maxval) - double(minval)) / range + double(minval); + return % scale_out diff --git a/lib/NiftiTools/view_nii_menu.m b/lib/NiftiTools/view_nii_menu.m index 2269c937..784f865d 100644 --- a/lib/NiftiTools/view_nii_menu.m +++ b/lib/NiftiTools/view_nii_menu.m @@ -5,476 +5,469 @@ % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % -%-------------------------------------------------------------------- +% -------------------------------------------------------------------- function menu_hdl = view_nii_menu(fig, varargin) - if isnumeric(fig) - menu_hdl = init(fig); - return; - end + if isnumeric(fig) + menu_hdl = init(fig); + return + end - menu_hdl = []; + menu_hdl = []; - switch fig - case 'interp' + switch fig + case 'interp' if nargin > 1 - fig = varargin{1}; + fig = varargin{1}; else - fig = gcbf; + fig = gcbf; end nii_menu = getappdata(fig, 'nii_menu'); - interp_on_state = get(nii_menu.Minterp,'Userdata'); + interp_on_state = get(nii_menu.Minterp, 'Userdata'); - if (interp_on_state == 1) - opt.useinterp = 1; - view_nii(fig,opt); - set(nii_menu.Minterp,'Userdata',0,'Label','Interp off'); - reset_zoom(fig); + if interp_on_state == 1 + opt.useinterp = 1; + view_nii(fig, opt); + set(nii_menu.Minterp, 'Userdata', 0, 'Label', 'Interp off'); + reset_zoom(fig); else - opt.useinterp = 0; - view_nii(fig,opt); - set(nii_menu.Minterp,'Userdata',1,'Label','Interp on'); - reset_zoom(fig); + opt.useinterp = 0; + view_nii(fig, opt); + set(nii_menu.Minterp, 'Userdata', 1, 'Label', 'Interp on'); + reset_zoom(fig); end - case 'reset_zoom' + case 'reset_zoom' if nargin > 1 - fig = varargin{1}; + fig = varargin{1}; else - fig = gcbf; + fig = gcbf; end reset_zoom(fig); - case 'orient' + case 'orient' orient; - case 'editvox' + case 'editvox' editvox; - case 'img_info' + case 'img_info' img_info; - case 'img_hist' + case 'img_hist' img_hist; - case 'save_disp' + case 'save_disp' save_disp; - end + end - return % view_nii_menu + return % view_nii_menu - -%-------------------------------------------------------------------- + % -------------------------------------------------------------------- function menu_hdl = init(fig) - % search for edit, view menu - % - nii_menu.Mfile = []; - nii_menu.Medit = []; - nii_menu.Mview = []; - menuitems = findobj(fig, 'type', 'uimenu'); - - for i=1:length(menuitems) - filelabel = get(menuitems(i),'label'); - - if strcmpi(strrep(filelabel, '&', ''), 'file') - nii_menu.Mfile = menuitems(i); - end - - editlabel = get(menuitems(i),'label'); - - if strcmpi(strrep(editlabel, '&', ''), 'edit') - nii_menu.Medit = menuitems(i); - end - - viewlabel = get(menuitems(i),'label'); - - if strcmpi(strrep(viewlabel, '&', ''), 'view') - nii_menu.Mview = menuitems(i); - end - end - - set(fig, 'menubar', 'none'); - - if isempty(nii_menu.Mfile) - nii_menu.Mfile = uimenu('Parent',fig, ... - 'Label','File'); - - nii_menu.Mfile_save = uimenu('Parent',nii_menu.Mfile, ... - 'Label','Save displayed image as ...', ... - 'Callback','view_nii_menu(''save_disp'');'); - else - nii_menu.Mfile_save = uimenu('Parent',nii_menu.Mfile, ... - 'Label','Save displayed image as ...', ... - 'separator','on', ... - 'Callback','view_nii_menu(''save_disp'');'); - end - - if isempty(nii_menu.Medit) - nii_menu.Medit = uimenu('Parent',fig, ... - 'Label','Edit'); - - nii_menu.Medit_orient = uimenu('Parent',nii_menu.Medit, ... - 'Label','Convert to RAS orientation', ... - 'Callback','view_nii_menu(''orient'');'); - - nii_menu.Medit_editvox = uimenu('Parent',nii_menu.Medit, ... - 'Label','Edit voxel value at crosshair', ... - 'Callback','view_nii_menu(''editvox'');'); - else - nii_menu.Medit_orient = uimenu('Parent',nii_menu.Medit, ... - 'Label','Convert to RAS orientation', ... - 'separator','on', ... - 'Callback','view_nii_menu(''orient'');'); - - nii_menu.Medit_editvox = uimenu('Parent',nii_menu.Medit, ... - 'Label','Edit voxel value at crosshair', ... - 'Callback','view_nii_menu(''editvox'');'); - end - - if isempty(nii_menu.Mview) - nii_menu.Mview = uimenu('Parent',fig, ... - 'Label','View'); - - nii_menu.Mview_info = uimenu('Parent',nii_menu.Mview, ... - 'Label','Image Information', ... - 'Callback','view_nii_menu(''img_info'');'); - - nii_menu.Mview_info = uimenu('Parent',nii_menu.Mview, ... - 'Label','Volume Histogram', ... - 'Callback','view_nii_menu(''img_hist'');'); - else - nii_menu.Mview_info = uimenu('Parent',nii_menu.Mview, ... - 'Label','Image Information', ... - 'separator','on', ... - 'Callback','view_nii_menu(''img_info'');'); - - nii_menu.Mview_info = uimenu('Parent',nii_menu.Mview, ... - 'Label','Volume Histogram', ... - 'Callback','view_nii_menu(''img_hist'');'); - end - - nii_menu.Mzoom = rri_zoom_menu(fig); - - nii_menu.Minterp = uimenu('Parent',fig, ... - 'Label','Interp on', ... - 'Userdata', 1, ... - 'Callback','view_nii_menu(''interp'');'); - - setappdata(fig,'nii_menu',nii_menu); - menu_hdl = nii_menu.Minterp; - - return % init - - -%---------------------------------------------------------------- + % search for edit, view menu + % + nii_menu.Mfile = []; + nii_menu.Medit = []; + nii_menu.Mview = []; + menuitems = findobj(fig, 'type', 'uimenu'); + + for i = 1:length(menuitems) + filelabel = get(menuitems(i), 'label'); + + if strcmpi(strrep(filelabel, '&', ''), 'file') + nii_menu.Mfile = menuitems(i); + end + + editlabel = get(menuitems(i), 'label'); + + if strcmpi(strrep(editlabel, '&', ''), 'edit') + nii_menu.Medit = menuitems(i); + end + + viewlabel = get(menuitems(i), 'label'); + + if strcmpi(strrep(viewlabel, '&', ''), 'view') + nii_menu.Mview = menuitems(i); + end + end + + set(fig, 'menubar', 'none'); + + if isempty(nii_menu.Mfile) + nii_menu.Mfile = uimenu('Parent', fig, ... + 'Label', 'File'); + + nii_menu.Mfile_save = uimenu('Parent', nii_menu.Mfile, ... + 'Label', 'Save displayed image as ...', ... + 'Callback', 'view_nii_menu(''save_disp'');'); + else + nii_menu.Mfile_save = uimenu('Parent', nii_menu.Mfile, ... + 'Label', 'Save displayed image as ...', ... + 'separator', 'on', ... + 'Callback', 'view_nii_menu(''save_disp'');'); + end + + if isempty(nii_menu.Medit) + nii_menu.Medit = uimenu('Parent', fig, ... + 'Label', 'Edit'); + + nii_menu.Medit_orient = uimenu('Parent', nii_menu.Medit, ... + 'Label', 'Convert to RAS orientation', ... + 'Callback', 'view_nii_menu(''orient'');'); + + nii_menu.Medit_editvox = uimenu('Parent', nii_menu.Medit, ... + 'Label', 'Edit voxel value at crosshair', ... + 'Callback', 'view_nii_menu(''editvox'');'); + else + nii_menu.Medit_orient = uimenu('Parent', nii_menu.Medit, ... + 'Label', 'Convert to RAS orientation', ... + 'separator', 'on', ... + 'Callback', 'view_nii_menu(''orient'');'); + + nii_menu.Medit_editvox = uimenu('Parent', nii_menu.Medit, ... + 'Label', 'Edit voxel value at crosshair', ... + 'Callback', 'view_nii_menu(''editvox'');'); + end + + if isempty(nii_menu.Mview) + nii_menu.Mview = uimenu('Parent', fig, ... + 'Label', 'View'); + + nii_menu.Mview_info = uimenu('Parent', nii_menu.Mview, ... + 'Label', 'Image Information', ... + 'Callback', 'view_nii_menu(''img_info'');'); + + nii_menu.Mview_info = uimenu('Parent', nii_menu.Mview, ... + 'Label', 'Volume Histogram', ... + 'Callback', 'view_nii_menu(''img_hist'');'); + else + nii_menu.Mview_info = uimenu('Parent', nii_menu.Mview, ... + 'Label', 'Image Information', ... + 'separator', 'on', ... + 'Callback', 'view_nii_menu(''img_info'');'); + + nii_menu.Mview_info = uimenu('Parent', nii_menu.Mview, ... + 'Label', 'Volume Histogram', ... + 'Callback', 'view_nii_menu(''img_hist'');'); + end + + nii_menu.Mzoom = rri_zoom_menu(fig); + + nii_menu.Minterp = uimenu('Parent', fig, ... + 'Label', 'Interp on', ... + 'Userdata', 1, ... + 'Callback', 'view_nii_menu(''interp'');'); + + setappdata(fig, 'nii_menu', nii_menu); + menu_hdl = nii_menu.Minterp; + + return % init + + % ---------------------------------------------------------------- function reset_zoom(fig) - old_handle_vis = get(fig, 'HandleVisibility'); - set(fig, 'HandleVisibility', 'on'); - - nii_view = getappdata(fig, 'nii_view'); - nii_menu = getappdata(fig, 'nii_menu'); + old_handle_vis = get(fig, 'HandleVisibility'); + set(fig, 'HandleVisibility', 'on'); - set(nii_menu.Mzoom,'Userdata',1,'Label','Zoom on'); - set(fig,'pointer','arrow'); - zoom off; + nii_view = getappdata(fig, 'nii_view'); + nii_menu = getappdata(fig, 'nii_menu'); - axes(nii_view.handles.axial_axes); - setappdata(get(gca,'zlabel'), 'ZOOMAxesData', ... - [get(gca, 'xlim') get(gca, 'ylim')]) -% zoom reset; - % zoom getlimits; - zoom out; + set(nii_menu.Mzoom, 'Userdata', 1, 'Label', 'Zoom on'); + set(fig, 'pointer', 'arrow'); + zoom off; - axes(nii_view.handles.coronal_axes); - setappdata(get(gca,'zlabel'), 'ZOOMAxesData', ... - [get(gca, 'xlim') get(gca, 'ylim')]) -% zoom reset; - % zoom getlimits; - zoom out; + axes(nii_view.handles.axial_axes); + setappdata(get(gca, 'zlabel'), 'ZOOMAxesData', ... + [get(gca, 'xlim') get(gca, 'ylim')]); + % zoom reset; + % zoom getlimits; + zoom out; - axes(nii_view.handles.sagittal_axes); - setappdata(get(gca,'zlabel'), 'ZOOMAxesData', ... - [get(gca, 'xlim') get(gca, 'ylim')]) -% zoom reset; - % zoom getlimits; - zoom out; + axes(nii_view.handles.coronal_axes); + setappdata(get(gca, 'zlabel'), 'ZOOMAxesData', ... + [get(gca, 'xlim') get(gca, 'ylim')]); + % zoom reset; + % zoom getlimits; + zoom out; - set(fig, 'HandleVisibility', old_handle_vis); + axes(nii_view.handles.sagittal_axes); + setappdata(get(gca, 'zlabel'), 'ZOOMAxesData', ... + [get(gca, 'xlim') get(gca, 'ylim')]); + % zoom reset; + % zoom getlimits; + zoom out; - return; % reset_zoom + set(fig, 'HandleVisibility', old_handle_vis); + return % reset_zoom -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function img_info - nii_view = getappdata(gcbf, 'nii_view'); - hdr = nii_view.nii.hdr; - - max_value = num2str(double(max(nii_view.nii.img(:)))); - min_value = num2str(double(min(nii_view.nii.img(:)))); - - dim = sprintf('%d %d %d', double(hdr.dime.dim(2:4))); - vox = sprintf('%.3f %.3f %.3f', double(hdr.dime.pixdim(2:4))); - - if double(hdr.dime.datatype) == 1 - type = '1-bit binary'; - elseif double(hdr.dime.datatype) == 2 - type = '8-bit unsigned integer'; - elseif double(hdr.dime.datatype) == 4 - type = '16-bit signed integer'; - elseif double(hdr.dime.datatype) == 8 - type = '32-bit signed integer'; - elseif double(hdr.dime.datatype) == 16 - type = '32-bit single float'; - elseif double(hdr.dime.datatype) == 64 - type = '64-bit double precision'; - elseif double(hdr.dime.datatype) == 128 - type = '24-bit RGB true color'; - elseif double(hdr.dime.datatype) == 256 - type = '8-bit signed integer'; - elseif double(hdr.dime.datatype) == 511 - type = '96-bit RGB true color'; - elseif double(hdr.dime.datatype) == 512 - type = '16-bit unsigned integer'; - elseif double(hdr.dime.datatype) == 768 - type = '32-bit unsigned integer'; - elseif double(hdr.dime.datatype) == 1024 - type = '64-bit signed integer'; - elseif double(hdr.dime.datatype) == 1280 - type = '64-bit unsigned integer'; - end - - msg = {}; - msg = [msg {''}]; - msg = [msg {['Dimension: [', dim, ']']}]; - msg = [msg {''}]; - msg = [msg {['Voxel Size: [', vox, ']']}]; - msg = [msg {''}]; - msg = [msg {['Data Type: [', type, ']']}]; - msg = [msg {''}]; - msg = [msg {['Max Value: [', max_value, ']']}]; - msg = [msg {''}]; - msg = [msg {['Min Value: [', min_value, ']']}]; - msg = [msg {''}]; - - if isfield(nii_view.nii, 'fileprefix') - if isfield(nii_view.nii, 'filetype') & nii_view.nii.filetype == 2 - msg = [msg {['File Name: [', nii_view.nii.fileprefix, '.nii]']}]; - msg = [msg {''}]; - elseif isfield(nii_view.nii, 'filetype') - msg = [msg {['File Name: [', nii_view.nii.fileprefix, '.img]']}]; - msg = [msg {''}]; - else - msg = [msg {['File Prefix: [', nii_view.nii.fileprefix, ']']}]; - msg = [msg {''}]; - end - end - - h = msgbox(msg, 'Image Information', 'modal'); - set(h,'color',[1 1 1]); - - return; % img_info - - -%---------------------------------------------------------------- + nii_view = getappdata(gcbf, 'nii_view'); + hdr = nii_view.nii.hdr; + + max_value = num2str(double(max(nii_view.nii.img(:)))); + min_value = num2str(double(min(nii_view.nii.img(:)))); + + dim = sprintf('%d %d %d', double(hdr.dime.dim(2:4))); + vox = sprintf('%.3f %.3f %.3f', double(hdr.dime.pixdim(2:4))); + + if double(hdr.dime.datatype) == 1 + type = '1-bit binary'; + elseif double(hdr.dime.datatype) == 2 + type = '8-bit unsigned integer'; + elseif double(hdr.dime.datatype) == 4 + type = '16-bit signed integer'; + elseif double(hdr.dime.datatype) == 8 + type = '32-bit signed integer'; + elseif double(hdr.dime.datatype) == 16 + type = '32-bit single float'; + elseif double(hdr.dime.datatype) == 64 + type = '64-bit double precision'; + elseif double(hdr.dime.datatype) == 128 + type = '24-bit RGB true color'; + elseif double(hdr.dime.datatype) == 256 + type = '8-bit signed integer'; + elseif double(hdr.dime.datatype) == 511 + type = '96-bit RGB true color'; + elseif double(hdr.dime.datatype) == 512 + type = '16-bit unsigned integer'; + elseif double(hdr.dime.datatype) == 768 + type = '32-bit unsigned integer'; + elseif double(hdr.dime.datatype) == 1024 + type = '64-bit signed integer'; + elseif double(hdr.dime.datatype) == 1280 + type = '64-bit unsigned integer'; + end + + msg = {}; + msg = [msg {''}]; + msg = [msg {['Dimension: [', dim, ']']}]; + msg = [msg {''}]; + msg = [msg {['Voxel Size: [', vox, ']']}]; + msg = [msg {''}]; + msg = [msg {['Data Type: [', type, ']']}]; + msg = [msg {''}]; + msg = [msg {['Max Value: [', max_value, ']']}]; + msg = [msg {''}]; + msg = [msg {['Min Value: [', min_value, ']']}]; + msg = [msg {''}]; + + if isfield(nii_view.nii, 'fileprefix') + if isfield(nii_view.nii, 'filetype') & nii_view.nii.filetype == 2 + msg = [msg {['File Name: [', nii_view.nii.fileprefix, '.nii]']}]; + msg = [msg {''}]; + elseif isfield(nii_view.nii, 'filetype') + msg = [msg {['File Name: [', nii_view.nii.fileprefix, '.img]']}]; + msg = [msg {''}]; + else + msg = [msg {['File Prefix: [', nii_view.nii.fileprefix, ']']}]; + msg = [msg {''}]; + end + end + + h = msgbox(msg, 'Image Information', 'modal'); + set(h, 'color', [1 1 1]); + + return % img_info + + % ---------------------------------------------------------------- function orient - fig = gcbf; - nii_view = getappdata(fig, 'nii_view'); - nii = nii_view.nii; - - if ~isempty(nii_view.bgimg) - msg = 'You can not modify an overlay image'; - h = msgbox(msg, 'Error', 'modal'); - return; - end - - old_pointer = get(fig,'Pointer'); - set(fig,'Pointer','watch'); - - [nii orient] = rri_orient(nii); - - if isequal(orient, [1 2 3]) % do nothing - set(fig,'Pointer',old_pointer); - return; - end - - oldopt = view_nii(fig); - opt.command = 'updatenii'; - opt.usecolorbar = oldopt.usecolorbar; - opt.usepanel = oldopt.usepanel; - opt.usecrosshair = oldopt.usecrosshair; - opt.usestretch = oldopt.usestretch; - opt.useimagesc = oldopt.useimagesc; - opt.useinterp = oldopt.useinterp; - opt.setarea = oldopt.area; - opt.setunit = oldopt.unit; - opt.setviewpoint = oldopt.viewpoint; - opt.setscanid = oldopt.scanid; - opt.setcbarminmax = oldopt.cbarminmax; - opt.setcolorindex = oldopt.colorindex; - opt.setcolormap = oldopt.colormap; - opt.setcolorlevel = oldopt.colorlevel; - - if isfield(oldopt,'highcolor') - opt.sethighcolor = oldopt.highcolor; - end - - view_nii(fig, nii, opt); - set(fig,'Pointer',old_pointer); - reset_zoom(fig); - - return; % orient - - -%---------------------------------------------------------------- + fig = gcbf; + nii_view = getappdata(fig, 'nii_view'); + nii = nii_view.nii; + + if ~isempty(nii_view.bgimg) + msg = 'You can not modify an overlay image'; + h = msgbox(msg, 'Error', 'modal'); + return + end + + old_pointer = get(fig, 'Pointer'); + set(fig, 'Pointer', 'watch'); + + [nii orient] = rri_orient(nii); + + if isequal(orient, [1 2 3]) % do nothing + set(fig, 'Pointer', old_pointer); + return + end + + oldopt = view_nii(fig); + opt.command = 'updatenii'; + opt.usecolorbar = oldopt.usecolorbar; + opt.usepanel = oldopt.usepanel; + opt.usecrosshair = oldopt.usecrosshair; + opt.usestretch = oldopt.usestretch; + opt.useimagesc = oldopt.useimagesc; + opt.useinterp = oldopt.useinterp; + opt.setarea = oldopt.area; + opt.setunit = oldopt.unit; + opt.setviewpoint = oldopt.viewpoint; + opt.setscanid = oldopt.scanid; + opt.setcbarminmax = oldopt.cbarminmax; + opt.setcolorindex = oldopt.colorindex; + opt.setcolormap = oldopt.colormap; + opt.setcolorlevel = oldopt.colorlevel; + + if isfield(oldopt, 'highcolor') + opt.sethighcolor = oldopt.highcolor; + end + + view_nii(fig, nii, opt); + set(fig, 'Pointer', old_pointer); + reset_zoom(fig); + + return % orient + + % ---------------------------------------------------------------- function editvox - fig = gcbf; - nii_view = getappdata(fig, 'nii_view'); - - if ~isempty(nii_view.bgimg) - msg = 'You can not modify an overlay image'; - h = msgbox(msg, 'Error', 'modal'); - return; - end - - nii = nii_view.nii; - oldopt = view_nii(fig); - sag = nii_view.imgXYZ.vox(1); - cor = nii_view.imgXYZ.vox(2); - axi = nii_view.imgXYZ.vox(3); - - if nii_view.nii.hdr.dime.datatype == 128 - imgvalue = [double(nii.img(sag,cor,axi,1,nii_view.scanid)) double(nii.img(sag,cor,axi,2,nii_view.scanid)) double(nii.img(sag,cor,axi,3,nii_view.scanid))]; - init_val = sprintf('%7.4g %7.4g %7.4g',imgvalue); - elseif nii_view.nii.hdr.dime.datatype == 511 - R = double(nii.img(sag,cor,axi,1,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - G = double(nii.img(sag,cor,axi,2,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - B = double(nii.img(sag,cor,axi,3,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - imgvalue = [R G B]; - init_val = sprintf('%7.4g %7.4g %7.4g',imgvalue); - else - imgvalue = double(nii.img(sag,cor,axi,nii_view.scanid)); - init_val = sprintf('%.6g',imgvalue); - end - - old_pointer = get(fig,'Pointer'); - set(fig,'Pointer','watch'); - - repeat = 1; - while repeat - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - init_val = inputdlg({'Replace the current voxel values with 3 new numbers:'}, ... - 'Edit voxel value at crosshair', 1, {num2str(init_val)}); - else - init_val = inputdlg({'Replace the current voxel value with 1 new number:'}, ... - 'Edit voxel value at crosshair', 1, {num2str(init_val)}); - end - - if isempty(init_val) - set(fig,'Pointer',old_pointer); - return - end - - imgvalue = str2num(init_val{1}); - - if ( (nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511) ... - & length(imgvalue) ~= 3 ) | ... - ( (nii_view.nii.hdr.dime.datatype ~= 128 & nii_view.nii.hdr.dime.datatype ~= 511) ... - & length(imgvalue) ~= 1 ) - % do nothing - else - repeat = 0; - end - end - - if nii_view.nii.hdr.dime.datatype == 128 - nii.img(sag,cor,axi,1,nii_view.scanid) = imgvalue(1); - nii.img(sag,cor,axi,2,nii_view.scanid) = imgvalue(2); - nii.img(sag,cor,axi,3,nii_view.scanid) = imgvalue(3); - elseif nii_view.nii.hdr.dime.datatype == 511 - nii.img(sag,cor,axi,1,nii_view.scanid) = (imgvalue(1) - nii_view.nii.hdr.dime.glmin) ... - / (nii_view.nii.hdr.dime.glmax - nii_view.nii.hdr.dime.glmin); - nii.img(sag,cor,axi,2,nii_view.scanid) = (imgvalue(2) - nii_view.nii.hdr.dime.glmin) ... - / (nii_view.nii.hdr.dime.glmax - nii_view.nii.hdr.dime.glmin); - nii.img(sag,cor,axi,3,nii_view.scanid) = (imgvalue(3) - nii_view.nii.hdr.dime.glmin) ... - / (nii_view.nii.hdr.dime.glmax - nii_view.nii.hdr.dime.glmin); - else - nii.img(sag,cor,axi,nii_view.scanid) = imgvalue; - end - - opt.command = 'updatenii'; - opt.usecolorbar = oldopt.usecolorbar; - opt.usepanel = oldopt.usepanel; - opt.usecrosshair = oldopt.usecrosshair; - opt.usestretch = oldopt.usestretch; - opt.useimagesc = oldopt.useimagesc; - opt.useinterp = oldopt.useinterp; - opt.setarea = oldopt.area; - opt.setunit = oldopt.unit; - opt.setviewpoint = oldopt.viewpoint; - opt.setscanid = oldopt.scanid; - opt.setcbarminmax = oldopt.cbarminmax; - opt.setcolorindex = oldopt.colorindex; - opt.setcolormap = oldopt.colormap; - opt.setcolorlevel = oldopt.colorlevel; - - if isfield(oldopt,'highcolor') - opt.sethighcolor = oldopt.highcolor; - end - - view_nii(fig, nii, opt); - set(fig,'Pointer',old_pointer); - reset_zoom(fig); - - return; % editvox - - -%---------------------------------------------------------------- + fig = gcbf; + nii_view = getappdata(fig, 'nii_view'); + + if ~isempty(nii_view.bgimg) + msg = 'You can not modify an overlay image'; + h = msgbox(msg, 'Error', 'modal'); + return + end + + nii = nii_view.nii; + oldopt = view_nii(fig); + sag = nii_view.imgXYZ.vox(1); + cor = nii_view.imgXYZ.vox(2); + axi = nii_view.imgXYZ.vox(3); + + if nii_view.nii.hdr.dime.datatype == 128 + imgvalue = [double(nii.img(sag, cor, axi, 1, nii_view.scanid)) double(nii.img(sag, cor, axi, 2, nii_view.scanid)) double(nii.img(sag, cor, axi, 3, nii_view.scanid))]; + init_val = sprintf('%7.4g %7.4g %7.4g', imgvalue); + elseif nii_view.nii.hdr.dime.datatype == 511 + R = double(nii.img(sag, cor, axi, 1, nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... + nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; + G = double(nii.img(sag, cor, axi, 2, nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... + nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; + B = double(nii.img(sag, cor, axi, 3, nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... + nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; + imgvalue = [R G B]; + init_val = sprintf('%7.4g %7.4g %7.4g', imgvalue); + else + imgvalue = double(nii.img(sag, cor, axi, nii_view.scanid)); + init_val = sprintf('%.6g', imgvalue); + end + + old_pointer = get(fig, 'Pointer'); + set(fig, 'Pointer', 'watch'); + + repeat = 1; + while repeat + if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 + init_val = inputdlg({'Replace the current voxel values with 3 new numbers:'}, ... + 'Edit voxel value at crosshair', 1, {num2str(init_val)}); + else + init_val = inputdlg({'Replace the current voxel value with 1 new number:'}, ... + 'Edit voxel value at crosshair', 1, {num2str(init_val)}); + end + + if isempty(init_val) + set(fig, 'Pointer', old_pointer); + return + end + + imgvalue = str2num(init_val{1}); + + if ((nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511) ... + & length(imgvalue) ~= 3) | ... + ((nii_view.nii.hdr.dime.datatype ~= 128 & nii_view.nii.hdr.dime.datatype ~= 511) ... + & length(imgvalue) ~= 1) + % do nothing + else + repeat = 0; + end + end + + if nii_view.nii.hdr.dime.datatype == 128 + nii.img(sag, cor, axi, 1, nii_view.scanid) = imgvalue(1); + nii.img(sag, cor, axi, 2, nii_view.scanid) = imgvalue(2); + nii.img(sag, cor, axi, 3, nii_view.scanid) = imgvalue(3); + elseif nii_view.nii.hdr.dime.datatype == 511 + nii.img(sag, cor, axi, 1, nii_view.scanid) = (imgvalue(1) - nii_view.nii.hdr.dime.glmin) ... + / (nii_view.nii.hdr.dime.glmax - nii_view.nii.hdr.dime.glmin); + nii.img(sag, cor, axi, 2, nii_view.scanid) = (imgvalue(2) - nii_view.nii.hdr.dime.glmin) ... + / (nii_view.nii.hdr.dime.glmax - nii_view.nii.hdr.dime.glmin); + nii.img(sag, cor, axi, 3, nii_view.scanid) = (imgvalue(3) - nii_view.nii.hdr.dime.glmin) ... + / (nii_view.nii.hdr.dime.glmax - nii_view.nii.hdr.dime.glmin); + else + nii.img(sag, cor, axi, nii_view.scanid) = imgvalue; + end + + opt.command = 'updatenii'; + opt.usecolorbar = oldopt.usecolorbar; + opt.usepanel = oldopt.usepanel; + opt.usecrosshair = oldopt.usecrosshair; + opt.usestretch = oldopt.usestretch; + opt.useimagesc = oldopt.useimagesc; + opt.useinterp = oldopt.useinterp; + opt.setarea = oldopt.area; + opt.setunit = oldopt.unit; + opt.setviewpoint = oldopt.viewpoint; + opt.setscanid = oldopt.scanid; + opt.setcbarminmax = oldopt.cbarminmax; + opt.setcolorindex = oldopt.colorindex; + opt.setcolormap = oldopt.colormap; + opt.setcolorlevel = oldopt.colorlevel; + + if isfield(oldopt, 'highcolor') + opt.sethighcolor = oldopt.highcolor; + end + + view_nii(fig, nii, opt); + set(fig, 'Pointer', old_pointer); + reset_zoom(fig); + + return % editvox + + % ---------------------------------------------------------------- function save_disp - [filename pathname] = uiputfile('*.*', 'Save displayed image as (*.nii or *.img)'); - - if isequal(filename,0) | isequal(pathname,0) - return; - else - out_imgfile = fullfile(pathname, filename); % original image file - end + [filename pathname] = uiputfile('*.*', 'Save displayed image as (*.nii or *.img)'); - old_pointer = get(gcbf,'Pointer'); - set(gcbf,'Pointer','watch'); + if isequal(filename, 0) | isequal(pathname, 0) + return + else + out_imgfile = fullfile(pathname, filename); % original image file + end - nii_view = getappdata(gcbf, 'nii_view'); - nii = nii_view.nii; + old_pointer = get(gcbf, 'Pointer'); + set(gcbf, 'Pointer', 'watch'); - try - save_nii(nii, out_imgfile); - catch - msg = 'File can not be saved.'; - msgbox(msg, 'File write error', 'modal'); - end + nii_view = getappdata(gcbf, 'nii_view'); + nii = nii_view.nii; - set(gcbf,'Pointer',old_pointer); + try + save_nii(nii, out_imgfile); + catch + msg = 'File can not be saved.'; + msgbox(msg, 'File write error', 'modal'); + end - return; % save_disp + set(gcbf, 'Pointer', old_pointer); + return % save_disp -%---------------------------------------------------------------- + % ---------------------------------------------------------------- function img_hist - nii_view = getappdata(gcbf, 'nii_view'); - N = hist(double(nii_view.nii.img(:)),256); - x = linspace(double(min(nii_view.nii.img(:))), double(max(nii_view.nii.img(:))), 256); - figure;bar(x,N); - set(gcf, 'number', 'off', 'name', 'Volume Histogram'); - set(gcf, 'windowstyle', 'modal'); % no zoom ... - - xspan = max(x) - min(x) + 1; - yspan = max(N) + 1; - set(gca, 'xlim', [min(x)-xspan/20, max(x)+xspan/20]); - set(gca, 'ylim', [-yspan/20, max(N)+yspan/20]); + nii_view = getappdata(gcbf, 'nii_view'); + N = hist(double(nii_view.nii.img(:)), 256); + x = linspace(double(min(nii_view.nii.img(:))), double(max(nii_view.nii.img(:))), 256); + figure; + bar(x, N); + set(gcf, 'number', 'off', 'name', 'Volume Histogram'); + set(gcf, 'windowstyle', 'modal'); % no zoom ... - return; % img_hist + xspan = max(x) - min(x) + 1; + yspan = max(N) + 1; + set(gca, 'xlim', [min(x) - xspan / 20, max(x) + xspan / 20]); + set(gca, 'ylim', [-yspan / 20, max(N) + yspan / 20]); + return % img_hist diff --git a/lib/NiftiTools/xform_nii.m b/lib/NiftiTools/xform_nii.m index 7252a7c5..0977fad6 100644 --- a/lib/NiftiTools/xform_nii.m +++ b/lib/NiftiTools/xform_nii.m @@ -2,8 +2,8 @@ % 'xform_nii.m' is an internal function called by "load_nii.m", so % you do not need run this program by yourself. It does simplified -% NIfTI sform/qform affine transform, and supports some of the -% affine transforms, including translation, reflection, and +% NIfTI sform/qform affine transform, and supports some of the +% affine transforms, including translation, reflection, and % orthogonal rotation (N*90 degree). % % For other affine transforms, e.g. any degree rotation, shearing @@ -14,7 +14,7 @@ % % Since 'xform_nii.m' does not involve any interpolation or any % slice change, the original image volume is supposed to be -% untouched, although it is translated, reflected, or even +% untouched, although it is translated, reflected, or even % orthogonally rotated, based on the affine matrix in the % NIfTI header. % @@ -30,7 +30,7 @@ % % Because 'reslice_nii.m' has to perform 3D interpolation, it can % be slow depending on image size and affine matrix in the header. -% +% % After you perform the affine transform, the 'nii' structure % generated from 'xform_nii.m' or new NIfTI file created from % 'reslice_nii.m' will be in RAS orientation, i.e. X axis from @@ -38,484 +38,482 @@ % from Inferior to Superior. % % NOTE: This function should be called immediately after load_nii. -% +% % Usage: [ nii ] = xform_nii(nii, [tolerance], [preferredForm]) -% -% nii - NIFTI structure (returned from load_nii) +% +% nii - NIFTI structure (returned from load_nii) % % tolerance (optional) - distortion allowed for non-orthogonal rotation -% or shearing in NIfTI affine matrix. It will be set to 0.1 (10%), -% if it is default or empty. +% or shearing in NIfTI affine matrix. It will be set to 0.1 (10%), +% if it is default or empty. % % preferredForm (optional) - selects which transformation from voxels -% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate -% "prefer sform or qform, but use others if preferred not present". -% Upper case indicate the program is forced to use the specificied -% tranform or fail loading. 'preferredForm' will be 's', if it is -% default or empty. - Jeff Gunter -% +% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate +% "prefer sform or qform, but use others if preferred not present". +% Upper case indicate the program is forced to use the specificied +% tranform or fail loading. 'preferredForm' will be 's', if it is +% default or empty. - Jeff Gunter +% % NIFTI data format can be found on: http://nifti.nimh.nih.gov % % - Jimmy Shen (jimmy@rotman-baycrest.on.ca) % function nii = xform_nii(nii, tolerance, preferredForm) - % save a copy of the header as it was loaded. This is the - % header before any sform, qform manipulation is done. - % - nii.original.hdr = nii.hdr; - - if ~exist('tolerance','var') | isempty(tolerance) - tolerance = 0.1; - elseif(tolerance<=0) - tolerance = eps; - end - - if ~exist('preferredForm','var') | isempty(preferredForm) - preferredForm= 's'; % Jeff - end - - % if scl_slope field is nonzero, then each voxel value in the - % dataset should be scaled as: y = scl_slope * x + scl_inter - % I bring it here because hdr will be modified by change_hdr. - % - if nii.hdr.dime.scl_slope ~= 0 & ... - ismember(nii.hdr.dime.datatype, [2,4,8,16,64,256,512,768]) & ... - (nii.hdr.dime.scl_slope ~= 1 | nii.hdr.dime.scl_inter ~= 0) - - nii.img = ... - nii.hdr.dime.scl_slope * double(nii.img) + nii.hdr.dime.scl_inter; - - if nii.hdr.dime.datatype == 64 - - nii.hdr.dime.datatype = 64; - nii.hdr.dime.bitpix = 64; - else - nii.img = single(nii.img); - - nii.hdr.dime.datatype = 16; - nii.hdr.dime.bitpix = 32; + % save a copy of the header as it was loaded. This is the + % header before any sform, qform manipulation is done. + % + nii.original.hdr = nii.hdr; + + if ~exist('tolerance', 'var') | isempty(tolerance) + tolerance = 0.1; + elseif tolerance <= 0 + tolerance = eps; + end + + if ~exist('preferredForm', 'var') | isempty(preferredForm) + preferredForm = 's'; % Jeff + end + + % if scl_slope field is nonzero, then each voxel value in the + % dataset should be scaled as: y = scl_slope * x + scl_inter + % I bring it here because hdr will be modified by change_hdr. + % + if nii.hdr.dime.scl_slope ~= 0 & ... + ismember(nii.hdr.dime.datatype, [2, 4, 8, 16, 64, 256, 512, 768]) & ... + (nii.hdr.dime.scl_slope ~= 1 | nii.hdr.dime.scl_inter ~= 0) + + nii.img = ... + nii.hdr.dime.scl_slope * double(nii.img) + nii.hdr.dime.scl_inter; + + if nii.hdr.dime.datatype == 64 + + nii.hdr.dime.datatype = 64; + nii.hdr.dime.bitpix = 64; + else + nii.img = single(nii.img); + + nii.hdr.dime.datatype = 16; + nii.hdr.dime.bitpix = 32; + end + + nii.hdr.dime.glmax = max(double(nii.img(:))); + nii.hdr.dime.glmin = min(double(nii.img(:))); + + % set scale to non-use, because it is applied in xform_nii + % + nii.hdr.dime.scl_slope = 0; + + end + + % However, the scaling is to be ignored if datatype is DT_RGB24. + + % If datatype is a complex type, then the scaling is to be applied + % to both the real and imaginary parts. + % + if nii.hdr.dime.scl_slope ~= 0 & ... + ismember(nii.hdr.dime.datatype, [32, 1792]) + + nii.img = ... + nii.hdr.dime.scl_slope * double(nii.img) + nii.hdr.dime.scl_inter; + + if nii.hdr.dime.datatype == 32 + nii.img = single(nii.img); + end + + nii.hdr.dime.glmax = max(double(nii.img(:))); + nii.hdr.dime.glmin = min(double(nii.img(:))); + + % set scale to non-use, because it is applied in xform_nii + % + nii.hdr.dime.scl_slope = 0; + + end + + % There is no need for this program to transform Analyze data + % + if nii.filetype == 0 & exist([nii.fileprefix '.mat'], 'file') + load([nii.fileprefix '.mat']); % old SPM affine matrix + R = M(1:3, 1:3); + T = M(1:3, 4); + T = R * ones(3, 1) + T; + M(1:3, 4) = T; + nii.hdr.hist.qform_code = 0; + nii.hdr.hist.sform_code = 1; + nii.hdr.hist.srow_x = M(1, :); + nii.hdr.hist.srow_y = M(2, :); + nii.hdr.hist.srow_z = M(3, :); + elseif nii.filetype == 0 + nii.hdr.hist.rot_orient = []; + nii.hdr.hist.flip_orient = []; + return % no sform/qform for Analyze format + end + + hdr = nii.hdr; + + [hdr, orient] = change_hdr(hdr, tolerance, preferredForm); + + % flip and/or rotate image data + % + if ~isequal(orient, [1 2 3]) + + old_dim = hdr.dime.dim([2:4]); + + % More than 1 time frame + % + if ndims(nii.img) > 3 + pattern = 1:prod(old_dim); + else + pattern = []; + end + + if ~isempty(pattern) + pattern = reshape(pattern, old_dim); + end + + % calculate for rotation after flip + % + rot_orient = mod(orient + 2, 3) + 1; + + % do flip: + % + flip_orient = orient - rot_orient; + + for i = 1:3 + if flip_orient(i) + if ~isempty(pattern) + pattern = flipdim(pattern, i); + else + nii.img = flipdim(nii.img, i); + end end + end - nii.hdr.dime.glmax = max(double(nii.img(:))); - nii.hdr.dime.glmin = min(double(nii.img(:))); - - % set scale to non-use, because it is applied in xform_nii - % - nii.hdr.dime.scl_slope = 0; - - end + % get index of orient (rotate inversely) + % + [tmp rot_orient] = sort(rot_orient); - % However, the scaling is to be ignored if datatype is DT_RGB24. + new_dim = old_dim; + new_dim = new_dim(rot_orient); + hdr.dime.dim([2:4]) = new_dim; - % If datatype is a complex type, then the scaling is to be applied - % to both the real and imaginary parts. - % - if nii.hdr.dime.scl_slope ~= 0 & ... - ismember(nii.hdr.dime.datatype, [32,1792]) + new_pixdim = hdr.dime.pixdim([2:4]); + new_pixdim = new_pixdim(rot_orient); + hdr.dime.pixdim([2:4]) = new_pixdim; - nii.img = ... - nii.hdr.dime.scl_slope * double(nii.img) + nii.hdr.dime.scl_inter; + % re-calculate originator + % + tmp = hdr.hist.originator([1:3]); + tmp = tmp(rot_orient); + flip_orient = flip_orient(rot_orient); - if nii.hdr.dime.datatype == 32 - nii.img = single(nii.img); + for i = 1:3 + if flip_orient(i) & ~isequal(tmp(i), 0) + tmp(i) = new_dim(i) - tmp(i) + 1; end + end - nii.hdr.dime.glmax = max(double(nii.img(:))); - nii.hdr.dime.glmin = min(double(nii.img(:))); - - % set scale to non-use, because it is applied in xform_nii - % - nii.hdr.dime.scl_slope = 0; - - end - - % There is no need for this program to transform Analyze data - % - if nii.filetype == 0 & exist([nii.fileprefix '.mat'],'file') - load([nii.fileprefix '.mat']); % old SPM affine matrix - R=M(1:3,1:3); - T=M(1:3,4); - T=R*ones(3,1)+T; - M(1:3,4)=T; - nii.hdr.hist.qform_code=0; - nii.hdr.hist.sform_code=1; - nii.hdr.hist.srow_x=M(1,:); - nii.hdr.hist.srow_y=M(2,:); - nii.hdr.hist.srow_z=M(3,:); - elseif nii.filetype == 0 - nii.hdr.hist.rot_orient = []; - nii.hdr.hist.flip_orient = []; - return; % no sform/qform for Analyze format - end - - hdr = nii.hdr; - - [hdr,orient]=change_hdr(hdr,tolerance,preferredForm); - - % flip and/or rotate image data - % - if ~isequal(orient, [1 2 3]) - - old_dim = hdr.dime.dim([2:4]); - - % More than 1 time frame - % - if ndims(nii.img) > 3 - pattern = 1:prod(old_dim); - else - pattern = []; - end - - if ~isempty(pattern) - pattern = reshape(pattern, old_dim); - end + hdr.hist.originator([1:3]) = tmp; + hdr.hist.rot_orient = rot_orient; + hdr.hist.flip_orient = flip_orient; - % calculate for rotation after flip - % - rot_orient = mod(orient + 2, 3) + 1; + % do rotation: + % + if ~isempty(pattern) + pattern = permute(pattern, rot_orient); + pattern = pattern(:); - % do flip: - % - flip_orient = orient - rot_orient; - - for i = 1:3 - if flip_orient(i) - if ~isempty(pattern) - pattern = flipdim(pattern, i); - else - nii.img = flipdim(nii.img, i); - end - end - end + if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 | ... + hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - % get index of orient (rotate inversely) - % - [tmp rot_orient] = sort(rot_orient); + tmp = reshape(nii.img(:, :, :, 1), [prod(new_dim) hdr.dime.dim(5:8)]); + tmp = tmp(pattern, :); + nii.img(:, :, :, 1) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]); - new_dim = old_dim; - new_dim = new_dim(rot_orient); - hdr.dime.dim([2:4]) = new_dim; + tmp = reshape(nii.img(:, :, :, 2), [prod(new_dim) hdr.dime.dim(5:8)]); + tmp = tmp(pattern, :); + nii.img(:, :, :, 2) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]); - new_pixdim = hdr.dime.pixdim([2:4]); - new_pixdim = new_pixdim(rot_orient); - hdr.dime.pixdim([2:4]) = new_pixdim; + if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 + tmp = reshape(nii.img(:, :, :, 3), [prod(new_dim) hdr.dime.dim(5:8)]); + tmp = tmp(pattern, :); + nii.img(:, :, :, 3) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]); + end - % re-calculate originator - % - tmp = hdr.hist.originator([1:3]); - tmp = tmp(rot_orient); - flip_orient = flip_orient(rot_orient); - - for i = 1:3 - if flip_orient(i) & ~isequal(tmp(i), 0) - tmp(i) = new_dim(i) - tmp(i) + 1; - end + else + nii.img = reshape(nii.img, [prod(new_dim) hdr.dime.dim(5:8)]); + nii.img = nii.img(pattern, :); + nii.img = reshape(nii.img, [new_dim hdr.dime.dim(5:8)]); end + else + if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 | ... + hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - hdr.hist.originator([1:3]) = tmp; - hdr.hist.rot_orient = rot_orient; - hdr.hist.flip_orient = flip_orient; + nii.img(:, :, :, 1) = permute(nii.img(:, :, :, 1), rot_orient); + nii.img(:, :, :, 2) = permute(nii.img(:, :, :, 2), rot_orient); - % do rotation: - % - if ~isempty(pattern) - pattern = permute(pattern, rot_orient); - pattern = pattern(:); - - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 | ... - hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - - tmp = reshape(nii.img(:,:,:,1), [prod(new_dim) hdr.dime.dim(5:8)]); - tmp = tmp(pattern, :); - nii.img(:,:,:,1) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]); - - tmp = reshape(nii.img(:,:,:,2), [prod(new_dim) hdr.dime.dim(5:8)]); - tmp = tmp(pattern, :); - nii.img(:,:,:,2) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]); - - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - tmp = reshape(nii.img(:,:,:,3), [prod(new_dim) hdr.dime.dim(5:8)]); - tmp = tmp(pattern, :); - nii.img(:,:,:,3) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]); - end - - else - nii.img = reshape(nii.img, [prod(new_dim) hdr.dime.dim(5:8)]); - nii.img = nii.img(pattern, :); - nii.img = reshape(nii.img, [new_dim hdr.dime.dim(5:8)]); - end + if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 + nii.img(:, :, :, 3) = permute(nii.img(:, :, :, 3), rot_orient); + end else - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 | ... - hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - - nii.img(:,:,:,1) = permute(nii.img(:,:,:,1), rot_orient); - nii.img(:,:,:,2) = permute(nii.img(:,:,:,2), rot_orient); - - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - nii.img(:,:,:,3) = permute(nii.img(:,:,:,3), rot_orient); - end - else - nii.img = permute(nii.img, rot_orient); - end + nii.img = permute(nii.img, rot_orient); end - else - hdr.hist.rot_orient = []; - hdr.hist.flip_orient = []; - end - - nii.hdr = hdr; + end + else + hdr.hist.rot_orient = []; + hdr.hist.flip_orient = []; + end - return; % xform_nii + nii.hdr = hdr; + return % xform_nii -%----------------------------------------------------------------------- + % ----------------------------------------------------------------------- function [hdr, orient] = change_hdr(hdr, tolerance, preferredForm) - orient = [1 2 3]; - affine_transform = 1; - - % NIFTI can have both sform and qform transform. This program - % will check sform_code prior to qform_code by default. - % - % If user specifys "preferredForm", user can then choose the - % priority. - Jeff - % - useForm=[]; % Jeff - - if isequal(preferredForm,'S') - if isequal(hdr.hist.sform_code,0) - error('User requires sform, sform not set in header'); - else - useForm='s'; - end - end % Jeff - - if isequal(preferredForm,'Q') - if isequal(hdr.hist.qform_code,0) - error('User requires qform, qform not set in header'); - else - useForm='q'; - end - end % Jeff - - if isequal(preferredForm,'s') - if hdr.hist.sform_code > 0 - useForm='s'; - elseif hdr.hist.qform_code > 0 - useForm='q'; - end - end % Jeff - - if isequal(preferredForm,'q') - if hdr.hist.qform_code > 0 - useForm='q'; - elseif hdr.hist.sform_code > 0 - useForm='s'; - end - end % Jeff - - if isequal(useForm,'s') - R = [hdr.hist.srow_x(1:3) - hdr.hist.srow_y(1:3) - hdr.hist.srow_z(1:3)]; - - T = [hdr.hist.srow_x(4) - hdr.hist.srow_y(4) - hdr.hist.srow_z(4)]; + orient = [1 2 3]; + affine_transform = 1; + + % NIFTI can have both sform and qform transform. This program + % will check sform_code prior to qform_code by default. + % + % If user specifys "preferredForm", user can then choose the + % priority. - Jeff + % + useForm = []; % Jeff + + if isequal(preferredForm, 'S') + if isequal(hdr.hist.sform_code, 0) + error('User requires sform, sform not set in header'); + else + useForm = 's'; + end + end % Jeff + + if isequal(preferredForm, 'Q') + if isequal(hdr.hist.qform_code, 0) + error('User requires qform, qform not set in header'); + else + useForm = 'q'; + end + end % Jeff + + if isequal(preferredForm, 's') + if hdr.hist.sform_code > 0 + useForm = 's'; + elseif hdr.hist.qform_code > 0 + useForm = 'q'; + end + end % Jeff + + if isequal(preferredForm, 'q') + if hdr.hist.qform_code > 0 + useForm = 'q'; + elseif hdr.hist.sform_code > 0 + useForm = 's'; + end + end % Jeff + + if isequal(useForm, 's') + R = [hdr.hist.srow_x(1:3) + hdr.hist.srow_y(1:3) + hdr.hist.srow_z(1:3)]; + + T = [hdr.hist.srow_x(4) + hdr.hist.srow_y(4) + hdr.hist.srow_z(4)]; + + if det(R) == 0 | ~isequal(R(find(R)), sum(R)') + hdr.hist.old_affine = [[R; [0 0 0]] [T; 1]]; + R_sort = sort(abs(R(:))); + R(find(abs(R) < tolerance * min(R_sort(end - 2:end)))) = 0; + hdr.hist.new_affine = [[R; [0 0 0]] [T; 1]]; if det(R) == 0 | ~isequal(R(find(R)), sum(R)') - hdr.hist.old_affine = [ [R;[0 0 0]] [T;1] ]; - R_sort = sort(abs(R(:))); - R( find( abs(R) < tolerance*min(R_sort(end-2:end)) ) ) = 0; - hdr.hist.new_affine = [ [R;[0 0 0]] [T;1] ]; - - if det(R) == 0 | ~isequal(R(find(R)), sum(R)') - msg = [char(10) char(10) ' Non-orthogonal rotation or shearing ']; - msg = [msg 'found inside the affine matrix' char(10)]; - msg = [msg ' in this NIfTI file. You have 3 options:' char(10) char(10)]; - msg = [msg ' 1. Using included ''reslice_nii.m'' program to reslice the NIfTI' char(10)]; - msg = [msg ' file. I strongly recommand this, because it will not cause' char(10)]; - msg = [msg ' negative effect, as long as you remember not to do slice' char(10)]; - msg = [msg ' time correction after using ''reslice_nii.m''.' char(10) char(10)]; - msg = [msg ' 2. Using included ''load_untouch_nii.m'' program to load image' char(10)]; - msg = [msg ' without applying any affine geometric transformation or' char(10)]; - msg = [msg ' voxel intensity scaling. This is only for people who want' char(10)]; - msg = [msg ' to do some image processing regardless of image orientation' char(10)]; - msg = [msg ' and to save data back with the same NIfTI header.' char(10) char(10)]; - msg = [msg ' 3. Increasing the tolerance to allow more distortion in loaded' char(10)]; - msg = [msg ' image, but I don''t suggest this.' char(10) char(10)]; - msg = [msg ' To get help, please type:' char(10) char(10) ' help reslice_nii.m' char(10)]; - msg = [msg ' help load_untouch_nii.m' char(10) ' help load_nii.m']; - error(msg); - end + msg = [char(10) char(10) ' Non-orthogonal rotation or shearing ']; + msg = [msg 'found inside the affine matrix' char(10)]; + msg = [msg ' in this NIfTI file. You have 3 options:' char(10) char(10)]; + msg = [msg ' 1. Using included ''reslice_nii.m'' program to reslice the NIfTI' char(10)]; + msg = [msg ' file. I strongly recommand this, because it will not cause' char(10)]; + msg = [msg ' negative effect, as long as you remember not to do slice' char(10)]; + msg = [msg ' time correction after using ''reslice_nii.m''.' char(10) char(10)]; + msg = [msg ' 2. Using included ''load_untouch_nii.m'' program to load image' char(10)]; + msg = [msg ' without applying any affine geometric transformation or' char(10)]; + msg = [msg ' voxel intensity scaling. This is only for people who want' char(10)]; + msg = [msg ' to do some image processing regardless of image orientation' char(10)]; + msg = [msg ' and to save data back with the same NIfTI header.' char(10) char(10)]; + msg = [msg ' 3. Increasing the tolerance to allow more distortion in loaded' char(10)]; + msg = [msg ' image, but I don''t suggest this.' char(10) char(10)]; + msg = [msg ' To get help, please type:' char(10) char(10) ' help reslice_nii.m' char(10)]; + msg = [msg ' help load_untouch_nii.m' char(10) ' help load_nii.m']; + error(msg); end + end + + elseif isequal(useForm, 'q') + b = hdr.hist.quatern_b; + c = hdr.hist.quatern_c; + d = hdr.hist.quatern_d; - elseif isequal(useForm,'q') - b = hdr.hist.quatern_b; - c = hdr.hist.quatern_c; - d = hdr.hist.quatern_d; - - if 1.0-(b*b+c*c+d*d) < 0 - if abs(1.0-(b*b+c*c+d*d)) < 1e-5 - a = 0; - else - error('Incorrect quaternion values in this NIFTI data.'); - end + if 1.0 - (b * b + c * c + d * d) < 0 + if abs(1.0 - (b * b + c * c + d * d)) < 1e-5 + a = 0; else - a = sqrt(1.0-(b*b+c*c+d*d)); + error('Incorrect quaternion values in this NIFTI data.'); end - - qfac = hdr.dime.pixdim(1); - if qfac==0, qfac = 1; end - i = hdr.dime.pixdim(2); - j = hdr.dime.pixdim(3); - k = qfac * hdr.dime.pixdim(4); - - R = [a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c - 2*b*c+2*a*d a*a+c*c-b*b-d*d 2*c*d-2*a*b - 2*b*d-2*a*c 2*c*d+2*a*b a*a+d*d-c*c-b*b]; - - T = [hdr.hist.qoffset_x - hdr.hist.qoffset_y - hdr.hist.qoffset_z]; - - % qforms are expected to generate rotation matrices R which are - % det(R) = 1; we'll make sure that happens. - % - % now we make the same checks as were done above for sform data - % BUT we do it on a transform that is in terms of voxels not mm; - % after we figure out the angles and squash them to closest - % rectilinear direction. After that, the voxel sizes are then - % added. - % - % This part is modified by Jeff Gunter. + else + a = sqrt(1.0 - (b * b + c * c + d * d)); + end + + qfac = hdr.dime.pixdim(1); + if qfac == 0 + qfac = 1; + end + i = hdr.dime.pixdim(2); + j = hdr.dime.pixdim(3); + k = qfac * hdr.dime.pixdim(4); + + R = [a * a + b * b - c * c - d * d 2 * b * c - 2 * a * d 2 * b * d + 2 * a * c + 2 * b * c + 2 * a * d a * a + c * c - b * b - d * d 2 * c * d - 2 * a * b + 2 * b * d - 2 * a * c 2 * c * d + 2 * a * b a * a + d * d - c * c - b * b]; + + T = [hdr.hist.qoffset_x + hdr.hist.qoffset_y + hdr.hist.qoffset_z]; + + % qforms are expected to generate rotation matrices R which are + % det(R) = 1; we'll make sure that happens. + % + % now we make the same checks as were done above for sform data + % BUT we do it on a transform that is in terms of voxels not mm; + % after we figure out the angles and squash them to closest + % rectilinear direction. After that, the voxel sizes are then + % added. + % + % This part is modified by Jeff Gunter. + % + if det(R) == 0 | ~isequal(R(find(R)), sum(R)') + + % det(R) == 0 is not a common trigger for this --- + % R(find(R)) is a list of non-zero elements in R; if that + % is straight (not oblique) then it should be the same as + % columnwise summation. Could just as well have checked the + % lengths of R(find(R)) and sum(R)' (which should be 3) % + hdr.hist.old_affine = [[R * diag([i j k]); [0 0 0]] [T; 1]]; + R_sort = sort(abs(R(:))); + R(find(abs(R) < tolerance * min(R_sort(end - 2:end)))) = 0; + R = R * diag([i j k]); + hdr.hist.new_affine = [[R; [0 0 0]] [T; 1]]; + if det(R) == 0 | ~isequal(R(find(R)), sum(R)') + msg = [char(10) char(10) ' Non-orthogonal rotation or shearing ']; + msg = [msg 'found inside the affine matrix' char(10)]; + msg = [msg ' in this NIfTI file. You have 3 options:' char(10) char(10)]; + msg = [msg ' 1. Using included ''reslice_nii.m'' program to reslice the NIfTI' char(10)]; + msg = [msg ' file. I strongly recommand this, because it will not cause' char(10)]; + msg = [msg ' negative effect, as long as you remember not to do slice' char(10)]; + msg = [msg ' time correction after using ''reslice_nii.m''.' char(10) char(10)]; + msg = [msg ' 2. Using included ''load_untouch_nii.m'' program to load image' char(10)]; + msg = [msg ' without applying any affine geometric transformation or' char(10)]; + msg = [msg ' voxel intensity scaling. This is only for people who want' char(10)]; + msg = [msg ' to do some image processing regardless of image orientation' char(10)]; + msg = [msg ' and to save data back with the same NIfTI header.' char(10) char(10)]; + msg = [msg ' 3. Increasing the tolerance to allow more distortion in loaded' char(10)]; + msg = [msg ' image, but I don''t suggest this.' char(10) char(10)]; + msg = [msg ' To get help, please type:' char(10) char(10) ' help reslice_nii.m' char(10)]; + msg = [msg ' help load_untouch_nii.m' char(10) ' help load_nii.m']; + error(msg); + end - % det(R) == 0 is not a common trigger for this --- - % R(find(R)) is a list of non-zero elements in R; if that - % is straight (not oblique) then it should be the same as - % columnwise summation. Could just as well have checked the - % lengths of R(find(R)) and sum(R)' (which should be 3) - % - hdr.hist.old_affine = [ [R * diag([i j k]);[0 0 0]] [T;1] ]; - R_sort = sort(abs(R(:))); - R( find( abs(R) < tolerance*min(R_sort(end-2:end)) ) ) = 0; - R = R * diag([i j k]); - hdr.hist.new_affine = [ [R;[0 0 0]] [T;1] ]; - - if det(R) == 0 | ~isequal(R(find(R)), sum(R)') - msg = [char(10) char(10) ' Non-orthogonal rotation or shearing ']; - msg = [msg 'found inside the affine matrix' char(10)]; - msg = [msg ' in this NIfTI file. You have 3 options:' char(10) char(10)]; - msg = [msg ' 1. Using included ''reslice_nii.m'' program to reslice the NIfTI' char(10)]; - msg = [msg ' file. I strongly recommand this, because it will not cause' char(10)]; - msg = [msg ' negative effect, as long as you remember not to do slice' char(10)]; - msg = [msg ' time correction after using ''reslice_nii.m''.' char(10) char(10)]; - msg = [msg ' 2. Using included ''load_untouch_nii.m'' program to load image' char(10)]; - msg = [msg ' without applying any affine geometric transformation or' char(10)]; - msg = [msg ' voxel intensity scaling. This is only for people who want' char(10)]; - msg = [msg ' to do some image processing regardless of image orientation' char(10)]; - msg = [msg ' and to save data back with the same NIfTI header.' char(10) char(10)]; - msg = [msg ' 3. Increasing the tolerance to allow more distortion in loaded' char(10)]; - msg = [msg ' image, but I don''t suggest this.' char(10) char(10)]; - msg = [msg ' To get help, please type:' char(10) char(10) ' help reslice_nii.m' char(10)]; - msg = [msg ' help load_untouch_nii.m' char(10) ' help load_nii.m']; - error(msg); - end + else + R = R * diag([i j k]); + end % 1st det(R) - else - R = R * diag([i j k]); - end % 1st det(R) + else + affine_transform = 0; % no sform or qform transform + end - else - affine_transform = 0; % no sform or qform transform - end + if affine_transform == 1 + voxel_size = abs(sum(R, 1)); + inv_R = inv(R); + originator = inv_R * (-T) + 1; + orient = get_orient(inv_R); - if affine_transform == 1 - voxel_size = abs(sum(R,1)); - inv_R = inv(R); - originator = inv_R*(-T)+1; - orient = get_orient(inv_R); + % modify pixdim and originator + % + hdr.dime.pixdim(2:4) = voxel_size; + hdr.hist.originator(1:3) = originator; - % modify pixdim and originator - % - hdr.dime.pixdim(2:4) = voxel_size; - hdr.hist.originator(1:3) = originator; + % set sform or qform to non-use, because they have been + % applied in xform_nii + % + hdr.hist.qform_code = 0; + hdr.hist.sform_code = 0; + end - % set sform or qform to non-use, because they have been - % applied in xform_nii - % - hdr.hist.qform_code = 0; - hdr.hist.sform_code = 0; - end + % apply space_unit to pixdim if not 1 (mm) + % + space_unit = get_units(hdr); - % apply space_unit to pixdim if not 1 (mm) - % - space_unit = get_units(hdr); + if space_unit ~= 1 + hdr.dime.pixdim(2:4) = hdr.dime.pixdim(2:4) * space_unit; - if space_unit ~= 1 - hdr.dime.pixdim(2:4) = hdr.dime.pixdim(2:4) * space_unit; + % set space_unit of xyzt_units to millimeter, because + % voxel_size has been re-scaled + % + hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units, 1, 0)); + hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units, 2, 1)); + hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units, 3, 0)); + end - % set space_unit of xyzt_units to millimeter, because - % voxel_size has been re-scaled - % - hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,1,0)); - hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,2,1)); - hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,3,0)); - end + hdr.dime.pixdim = abs(hdr.dime.pixdim); - hdr.dime.pixdim = abs(hdr.dime.pixdim); + return % change_hdr - return; % change_hdr - - -%----------------------------------------------------------------------- + % ----------------------------------------------------------------------- function orient = get_orient(R) - orient = []; + orient = []; - for i = 1:3 - switch find(R(i,:)) * sign(sum(R(i,:))) + for i = 1:3 + switch find(R(i, :)) * sign(sum(R(i, :))) case 1 - orient = [orient 1]; % Left to Right + orient = [orient 1]; % Left to Right case 2 - orient = [orient 2]; % Posterior to Anterior + orient = [orient 2]; % Posterior to Anterior case 3 - orient = [orient 3]; % Inferior to Superior + orient = [orient 3]; % Inferior to Superior case -1 - orient = [orient 4]; % Right to Left + orient = [orient 4]; % Right to Left case -2 - orient = [orient 5]; % Anterior to Posterior + orient = [orient 5]; % Anterior to Posterior case -3 - orient = [orient 6]; % Superior to Inferior - end - end + orient = [orient 6]; % Superior to Inferior + end + end - return; % get_orient + return % get_orient - -%----------------------------------------------------------------------- + % ----------------------------------------------------------------------- function [space_unit, time_unit] = get_units(hdr) - switch bitand(hdr.dime.xyzt_units, 7) % mask with 0x07 - case 1 - space_unit = 1e+3; % meter, m - case 3 - space_unit = 1e-3; % micrometer, um - otherwise - space_unit = 1; % millimeter, mm - end - - switch bitand(hdr.dime.xyzt_units, 56) % mask with 0x38 - case 16 - time_unit = 1e-3; % millisecond, ms - case 24 - time_unit = 1e-6; % microsecond, us - otherwise - time_unit = 1; % second, s - end - - return; % get_units - + switch bitand(hdr.dime.xyzt_units, 7) % mask with 0x07 + case 1 + space_unit = 1e+3; % meter, m + case 3 + space_unit = 1e-3; % micrometer, um + otherwise + space_unit = 1; % millimeter, mm + end + + switch bitand(hdr.dime.xyzt_units, 56) % mask with 0x38 + case 16 + time_unit = 1e-3; % millisecond, ms + case 24 + time_unit = 1e-6; % microsecond, us + otherwise + time_unit = 1; % second, s + end + + return % get_units diff --git a/lib/mancoreg/mancoregCallbacks.m b/lib/mancoreg/mancoregCallbacks.m index c92167bf..40593bd5 100644 --- a/lib/mancoreg/mancoregCallbacks.m +++ b/lib/mancoreg/mancoregCallbacks.m @@ -64,7 +64,7 @@ function mancoregCallbacks(operation) function moveImage() - global st mancoregvar; + global st mancoregvar angl_pitch = get(mancoregvar.hpitch, 'Value'); angl_roll = get(mancoregvar.hroll, 'Value'); @@ -96,7 +96,7 @@ function moveImage() function toggleOff() - global st mancoregvar; + global st mancoregvar if get(mancoregvar.htoggle_off, 'value') == 0 % Source is to be displayed @@ -114,7 +114,7 @@ function toggleOff() function toggleOn() - global st mancoregvar; + global st mancoregvar if get(mancoregvar.htoggle_on, 'value') == 0 % Source is to be displayed @@ -135,7 +135,7 @@ function toggleOn() function resetTransformationMatrix() - global st mancoregvar; + global st mancoregvar set(mancoregvar.hpitch, 'Value', 0); set(mancoregvar.hroll, 'Value', 0); @@ -165,7 +165,7 @@ function resetTransformationMatrix() function applyTransformationMatrix() - global st mancoregvar; + global st mancoregvar angl_pitch = get(mancoregvar.hpitch, 'Value'); angl_roll = get(mancoregvar.hroll, 'Value'); @@ -218,7 +218,7 @@ function applyTransformationMatrix() function plotMat - global mancoregvar; + global mancoregvar angl_pitch = get(mancoregvar.hpitch, 'Value'); angl_roll = get(mancoregvar.hroll, 'Value'); diff --git a/lib/panel-2.14/demo/demopanel1.m b/lib/panel-2.14/demo/demopanel1.m index c920b3ef..abc25727 100644 --- a/lib/panel-2.14/demo/demopanel1.m +++ b/lib/panel-2.14/demo/demopanel1.m @@ -1,4 +1,3 @@ - % What can Panel do? % % This demo just shows off what Panel can do. It is not @@ -10,65 +9,61 @@ % % Now, move on to demopanel2 to learn how to use panel. - - %% (a) % clf -figure(1) -clf +figure(1); +clf; % create panel p = panel(); % layout a variety of sub-panels -p.pack('h', {1/3 []}) -p(1).pack({2/3 []}); -p(1,1).pack(3, 2); +p.pack('h', {1 / 3 []}); +p(1).pack({2 / 3 []}); +p(1, 1).pack(3, 2); p(2).pack(6, 2); % set margins p.de.margin = 2; -p(1,1).marginbottom = 12; +p(1, 1).marginbottom = 12; p(2).marginleft = 20; p.margin = [13 10 2 2]; % and some properties p.fontsize = 8; - - %% (b) % data set 1 for m = 1:3 - for n = 1:2 - - % prepare sample data - t = (0:99) / 100; - s1 = sin(t * 2 * pi * m); - s2 = sin(t * 2 * pi * n * 2); - - % select axis - see data set 2 for an alternative way to - % access sub-panels - p(1,1,m,n).select(); - - % plot - plot(t, s1, 'r', 'linewidth', 1); - hold on - plot(t, s2, 'b', 'linewidth', 1); - plot(t, s1+s2, 'k', 'linewidth', 1); - - % finalise axis - axis([0 1 -2.2 2.2]); - set(gca, 'xtick', [], 'ytick', []); - - end + for n = 1:2 + + % prepare sample data + t = (0:99) / 100; + s1 = sin(t * 2 * pi * m); + s2 = sin(t * 2 * pi * n * 2); + + % select axis - see data set 2 for an alternative way to + % access sub-panels + p(1, 1, m, n).select(); + + % plot + plot(t, s1, 'r', 'linewidth', 1); + hold on; + plot(t, s2, 'b', 'linewidth', 1); + plot(t, s1 + s2, 'k', 'linewidth', 1); + + % finalise axis + axis([0 1 -2.2 2.2]); + set(gca, 'xtick', [], 'ytick', []); + + end end % label axis group -p(1,1).xlabel('time (unitless)'); -p(1,1).ylabel('example data series'); +p(1, 1).xlabel('time (unitless)'); +p(1, 1).ylabel('example data series'); % data set 2 source = 'XYZXYZ'; @@ -79,32 +74,32 @@ % loop for m = 1:6 - for n = 1:2 - - % select axis - these two lines do the same thing (see - % above) -% p(2, m, n).select(); - q(m, n).select(); - - % prepare sample data - data = randn(100, 1) * 0.4; - - % do stats - stats = []; - stats.source = source(m); - stats.binrange = [-1 1]; - stats.xtick = [-0.8:0.4:0.8]; - stats.ytick = [0 20]; - stats.bincens = -0.9:0.2:0.9; - stats.values = data; - stats.freq = hist(data, stats.bincens); - stats.percfreq = stats.freq / length(data) * 100; - stats.percpeak = 30; - - % plot - demopanel_minihist(stats, m == 6, n == 1); - - end + for n = 1:2 + + % select axis - these two lines do the same thing (see + % above) + % p(2, m, n).select(); + q(m, n).select(); + + % prepare sample data + data = randn(100, 1) * 0.4; + + % do stats + stats = []; + stats.source = source(m); + stats.binrange = [-1 1]; + stats.xtick = [-0.8:0.4:0.8]; + stats.ytick = [0 20]; + stats.bincens = -0.9:0.2:0.9; + stats.values = data; + stats.freq = hist(data, stats.bincens); + stats.percfreq = stats.freq / length(data) * 100; + stats.percpeak = 30; + + % plot + demopanel_minihist(stats, m == 6, n == 1); + + end end % label axis group @@ -119,12 +114,10 @@ r2 = randn(100, 1); % plot -plot(r1, r1+0.2*r2, 'k.') -hold on -plot([0 1], [0 1], 'r-') +plot(r1, r1 + 0.2 * r2, 'k.'); +hold on; +plot([0 1], [0 1], 'r-'); % finalise axis xlabel('our predictions'); -ylabel('actual measurements') - - +ylabel('actual measurements'); diff --git a/lib/panel-2.14/demo/demopanel2.m b/lib/panel-2.14/demo/demopanel2.m index dce29fd4..665a794e 100644 --- a/lib/panel-2.14/demo/demopanel2.m +++ b/lib/panel-2.14/demo/demopanel2.m @@ -1,11 +1,8 @@ - % Basic use. Panel is just like subplot. % % (a) Create a grid of panels. % (b) Plot into each sub-panel. - - %% (a) % create a NxN grid in gcf (this will create a figure, if @@ -22,43 +19,38 @@ N = 2; use_panel = 1; -clf +clf; % PREPARE if use_panel - p = panel(); - p.pack(N, N); + p = panel(); + p.pack(N, N); end - - %% (b) % plot into each panel in turn for m = 1:N - for n = 1:N - - % select one of the NxN grid of sub-panels - if use_panel - p(m, n).select(); - else - subplot(N, N, m + (n-1) * N); - end - - % plot some data - plot(randn(100,1)); - - % you can use all the usual calls - xlabel('sample number'); - ylabel('data'); - - % and so on - generally, you can treat the axis panel - % like any other axis - axis([0 100 -3 3]); - - end -end + for n = 1:N + + % select one of the NxN grid of sub-panels + if use_panel + p(m, n).select(); + else + subplot(N, N, m + (n - 1) * N); + end + % plot some data + plot(randn(100, 1)); + % you can use all the usual calls + xlabel('sample number'); + ylabel('data'); + % and so on - generally, you can treat the axis panel + % like any other axis + axis([0 100 -3 3]); + + end +end diff --git a/lib/panel-2.14/demo/demopanel3.m b/lib/panel-2.14/demo/demopanel3.m index 21d0ed16..f0fcb180 100644 --- a/lib/panel-2.14/demo/demopanel3.m +++ b/lib/panel-2.14/demo/demopanel3.m @@ -1,4 +1,3 @@ - % You can nest Panels as much as you like. % % (a) Create a grid of panels. @@ -6,8 +5,6 @@ % (c) Create another grid in the fourth. % (d) Plot into each of these. - - %% (a) % create a panel in gcf. @@ -20,33 +17,29 @@ % pack a 2x2 grid of panels into it. p.pack(2, 2); - - %% (b) % plot into the first three panels for m = 1:2 - for n = 1:2 - - % skip the 2,2 panel - if m == 2 && n == 2 - break - end - - % select the panel (create an axis, and make that axis - % current) - p(m, n).select(); - - % plot some stuff - plot(randn(100,1)); - xlabel('sample number'); - ylabel('data'); - axis([0 100 -3 3]); - - end -end + for n = 1:2 + + % skip the 2,2 panel + if m == 2 && n == 2 + break + end + % select the panel (create an axis, and make that axis + % current) + p(m, n).select(); + % plot some stuff + plot(randn(100, 1)); + xlabel('sample number'); + ylabel('data'); + axis([0 100 -3 3]); + + end +end %% (c) @@ -62,24 +55,22 @@ % children it creates all start as uncommitted panels. p(2, 2).pack(2, 3); - - %% (d) % plot into the six new sub-sub-panels for m = 1:2 - for n = 1:3 - - % select the panel - this commits it as an axis panel - p(2, 2, m, n).select(); - - % plot some stuff - plot(randn(100,1)); - xlabel('sample number'); - ylabel('data'); - axis([0 100 -3 3]); - - end + for n = 1:3 + + % select the panel - this commits it as an axis panel + p(2, 2, m, n).select(); + + % plot some stuff + plot(randn(100, 1)); + xlabel('sample number'); + ylabel('data'); + axis([0 100 -3 3]); + + end end % note this alternative, equivalent, way to reference a @@ -88,19 +79,14 @@ % plot another bit of data into the six sub-sub-panels for m = 1:2 - for n = 1:3 - - % select the panel - p_22(m, n).select(); - - % plot more stuff - hold on - plot(randn(100,1)*0.3, 'r'); - - end -end - - + for n = 1:3 + % select the panel + p_22(m, n).select(); + % plot more stuff + hold on; + plot(randn(100, 1) * 0.3, 'r'); + end +end diff --git a/lib/panel-2.14/demo/demopanel4.m b/lib/panel-2.14/demo/demopanel4.m index 41b83536..b2301e5d 100644 --- a/lib/panel-2.14/demo/demopanel4.m +++ b/lib/panel-2.14/demo/demopanel4.m @@ -1,4 +1,3 @@ - % Panels can be any size. % % (a) Create an asymmetrical grid of panels. @@ -6,17 +5,13 @@ % (c) Use select('all') to load them all with axes % (d) Get handles to all the axes and modify them. - - %% (a) % create a 2x2 grid in gcf with different fractionally-sized % rows and columns. a row or column sized as "[]" will % stretch to fill the remaining unassigned space. p = panel(); -p.pack({1/3 []}, {1/3 []}); - - +p.pack({1 / 3 []}, {1 / 3 []}); %% (b) @@ -26,8 +21,6 @@ % 100 for percentage, or 0 to 1 for fraction). p(2, 2).pack({30 70}, {20 20 []}); - - %% (c) % use select('all') to quickly show the layout you've achieved. @@ -40,8 +33,6 @@ % you to see what you're doing as you're starting out. p.select('all'); - - %% (d) % whilst we're here, we can get all the axes within a @@ -56,4 +47,3 @@ set(h_axes, 'color', [0 0 0]); % yeah, real gothic. - diff --git a/lib/panel-2.14/demo/demopanel5.m b/lib/panel-2.14/demo/demopanel5.m index 9074da4a..53e76604 100644 --- a/lib/panel-2.14/demo/demopanel5.m +++ b/lib/panel-2.14/demo/demopanel5.m @@ -1,33 +1,28 @@ - % Tools for finding your way around a layout. % % (a) Recreate the complex layout from demopanel1 % (b) Show three tools that help to navigate a layout - - %% (a) % create panel p = panel(); % layout a variety of sub-panels -p.pack('h', {1/3 []}) -p(1).pack({2/3 []}); -p(1,1).pack(3, 2); +p.pack('h', {1 / 3 []}); +p(1).pack({2 / 3 []}); +p(1, 1).pack(3, 2); p(2).pack(6, 2); % set margins p.de.margin = 10; -p(1,1).marginbottom = 20; +p(1, 1).marginbottom = 20; p(2).marginleft = 20; p.margin = [13 10 2 2]; % set some font properties p.fontsize = 8; - - %% (b) % if a layout gets complex, it can be tricky to find your @@ -46,7 +41,7 @@ % is because they are "object panels", which is the general % name for axis panels (and that's because panels can contain % other graphics objects as well as axes). -p +p; % use identify() % @@ -61,14 +56,10 @@ % we can demonstrate this by using this tool. the selected % panel is highlighted in red. show works on parent panels % as well - try "p(2).show()", for instance. -p(2,6,2).show(); +p(2, 6, 2).show(); % just to prove the point, let's now select one of the % panels we've identified and plot something into it. -p(2,4,1).select(); -plot(randn(100, 1)) -axis auto - - - - +p(2, 4, 1).select(); +plot(randn(100, 1)); +axis auto; diff --git a/lib/panel-2.14/demo/demopanel6.m b/lib/panel-2.14/demo/demopanel6.m index 18499f54..693d3af8 100644 --- a/lib/panel-2.14/demo/demopanel6.m +++ b/lib/panel-2.14/demo/demopanel6.m @@ -1,4 +1,3 @@ - % Packing is very flexible - it doesn't just do grids. % % (a) Pack a pair of columns. @@ -6,8 +5,6 @@ % (c) Pack into the other using absolute packing mode. % (d) Call select('all'), to show off the result. - - %% (a) % create the root panel, and pack two columns. to pack @@ -16,12 +13,10 @@ p = panel(); p.pack('h', 2); - - %% (b) % pack some stuff into the left column. -p(1).pack({1/6 1/6 1/6}); +p(1).pack({1 / 6 1 / 6 1 / 6}); % oops, we didn't fill the thing. let's finish that off with % a couple of panels that are streeeeeeeee-tchy... @@ -39,8 +34,6 @@ % see help panel/pack or doc panel for more information on % the packing possibilities. - - %% (c) % in the other column, we'll show how to do absolute mode @@ -64,8 +57,6 @@ % see help panel/pack or doc panel for more information on % the packing possibilities. - - %% (d) % use selectAll to quickly show the layout you've achieved. @@ -73,10 +64,3 @@ % they can't be parents anymore (i.e. they can't have more % children pack()ed into them). p.select('all'); - - - - - - - diff --git a/lib/panel-2.14/demo/demopanel7.m b/lib/panel-2.14/demo/demopanel7.m index ea883197..91038733 100644 --- a/lib/panel-2.14/demo/demopanel7.m +++ b/lib/panel-2.14/demo/demopanel7.m @@ -1,11 +1,8 @@ - % Panel gives you figure-wide control over text properties. % % (a) Create a grid of panels. % (b) Change some text properties. - - %% (a) % create a grid @@ -15,10 +12,6 @@ % select all p.select('all'); - - - - %% (b) % if we set the properties on the root panel, they affect @@ -29,7 +22,4 @@ % however, any child can override them, and the changes % affect just that child (and its descendants). -p(2,2).fontsize = 14; - - - +p(2, 2).fontsize = 14; diff --git a/lib/panel-2.14/demo/demopanel8.m b/lib/panel-2.14/demo/demopanel8.m index 266c7d8a..bdfc2beb 100644 --- a/lib/panel-2.14/demo/demopanel8.m +++ b/lib/panel-2.14/demo/demopanel8.m @@ -1,11 +1,8 @@ - % You can repack Panels from the command line. % % (a) Create a grid of panels, and show something in them. % (b) Repack some of them, as if at the command line. - - %% (a) % create a 2x2 grid in gcf. @@ -14,13 +11,11 @@ % have a look at p - all the child panels are currently % uncommitted -p +p; % commit all the uncommitted panels as axis panels p.select('all'); - - %% (b) % during development of a layout, you might find repack() @@ -34,7 +29,4 @@ % remember, you can always get a summary of the layout by % looking at the root panel in the command window -p - - - +p; diff --git a/lib/panel-2.14/demo/demopanel9.m b/lib/panel-2.14/demo/demopanel9.m index 52100d77..cb924221 100644 --- a/lib/panel-2.14/demo/demopanel9.m +++ b/lib/panel-2.14/demo/demopanel9.m @@ -1,4 +1,3 @@ - % Panel can build complex layouts rapidly (HINTS on MARGINS!). % % (a) Build the layout from demopanel1, with annotation @@ -6,90 +5,86 @@ % (c) Show labelling of axis groups % (d) Add appropriate margins for this layout - - %% (a) % create panel p = panel(); % let's start with two columns, one third and two thirds -p.pack('h', {1/3 2/3}) +p.pack('h', {1 / 3 2 / 3}); % then let's pack two rows into the first column, with the % top row pretty big so we've room for some sub-panels -p(1).pack({2/3 []}); +p(1).pack({2 / 3 []}); % now let's pack in those sub-panels -p(1,1).pack(3, 2); +p(1, 1).pack(3, 2); % finally, let's pack a grid of sub-panels into the right % hand side too p(2).pack(6, 2); - - %% (b) % now, let's populate those panels with axes full of data... % data set 1 for m = 1:3 - for n = 1:2 - - % prepare sample data - t = (0:99) / 100; - s1 = sin(t * 2 * pi * m); - s2 = sin(t * 2 * pi * n * 2); - - % select axis - p(1,1,m,n).select(); - - % NB: an alternative way of accessing - % q = p(1, 1); - % q(m, n).select(); - - % plot - plot(t, s1, 'r', 'linewidth', 1); - hold on - plot(t, s2, 'b', 'linewidth', 1); - plot(t, s1+s2, 'k', 'linewidth', 1); - - % finalise axis - axis([0 1 -2.2 2.2]); - set(gca, 'xtick', [], 'ytick', []); - - end + for n = 1:2 + + % prepare sample data + t = (0:99) / 100; + s1 = sin(t * 2 * pi * m); + s2 = sin(t * 2 * pi * n * 2); + + % select axis + p(1, 1, m, n).select(); + + % NB: an alternative way of accessing + % q = p(1, 1); + % q(m, n).select(); + + % plot + plot(t, s1, 'r', 'linewidth', 1); + hold on; + plot(t, s2, 'b', 'linewidth', 1); + plot(t, s1 + s2, 'k', 'linewidth', 1); + + % finalise axis + axis([0 1 -2.2 2.2]); + set(gca, 'xtick', [], 'ytick', []); + + end end % data set 2 source = 'XYZXYZ'; for m = 1:6 - for n = 1:2 - - % select axis - p(2,m,n).select(); - - % prepare sample data - data = randn(100, 1) * 0.4; - - % do stats - stats = []; - stats.source = source(m); - stats.binrange = [-1 1]; - stats.xtick = [-0.8:0.4:0.8]; - stats.ytick = [0 20 40]; - stats.bincens = -0.9:0.2:0.9; - stats.values = data; - stats.freq = hist(data, stats.bincens); - stats.percfreq = stats.freq / length(data) * 100; - stats.percpeak = 30; - - % plot - demopanel_minihist(stats, m == 6, n == 1); - - end + for n = 1:2 + + % select axis + p(2, m, n).select(); + + % prepare sample data + data = randn(100, 1) * 0.4; + + % do stats + stats = []; + stats.source = source(m); + stats.binrange = [-1 1]; + stats.xtick = [-0.8:0.4:0.8]; + stats.ytick = [0 20 40]; + stats.bincens = -0.9:0.2:0.9; + stats.values = data; + stats.freq = hist(data, stats.bincens); + stats.percfreq = stats.freq / length(data) * 100; + stats.percpeak = 30; + + % plot + demopanel_minihist(stats, m == 6, n == 1); + + end end % data set 3 @@ -100,15 +95,13 @@ r2 = randn(100, 1); % plot -plot(r1, r1+0.2*r2, 'k.') -hold on -plot([0 1], [0 1], 'r-') +plot(r1, r1 + 0.2 * r2, 'k.'); +hold on; +plot([0 1], [0 1], 'r-'); % finalise axis xlabel('our predictions'); -ylabel('actual measurements') - - +ylabel('actual measurements'); %% (c) @@ -117,8 +110,8 @@ % from panel, rather than the matlab call xlabel(). % label axis group -p(1,1).xlabel('time (unitless)'); -p(1,1).ylabel('example data series'); +p(1, 1).xlabel('time (unitless)'); +p(1, 1).ylabel('example data series'); % we can also get a handle back to the label object, so % that we can access its properties. @@ -130,16 +123,12 @@ % access properties % get(h, ... - - %% (d) % wow, those default margins suck for this figure. let's see % if we can do better... disp('These are the default margins - press any key to continue...'); -pause - - +pause; %%%% STEP 1 : TIGHT INTERNAL MARGINS @@ -162,23 +151,19 @@ % pause disp('We''ve tightened internal margins - press any key to continue...'); -pause - - +pause; %%%% STEP 2 : INCREASE INTERNAL MARGINS AS REQUIRED % now, let's space out the places we want spaced out - % remember that you can use p.identify() to get a nice % indication of how to reference individual panels. -p(1,1).marginbottom = 12; +p(1, 1).marginbottom = 12; p(2).marginleft = 20; % pause disp('We''ve increased two internal margins - press any key to continue...'); -pause - - +pause; %%%% STEP 3 : FINALISE MARGINS WITH FIGURE EDGES @@ -194,8 +179,3 @@ % report disp('We''ve now adjusted the figure edge margins (and reduced the fontsize), so we''re done.'); - - - - - diff --git a/lib/panel-2.14/demo/demopanelA.m b/lib/panel-2.14/demo/demopanelA.m index 8e4a5bd8..504ca996 100644 --- a/lib/panel-2.14/demo/demopanelA.m +++ b/lib/panel-2.14/demo/demopanelA.m @@ -1,4 +1,3 @@ - % Panel builds image files, not just on-screen figures. % % (a) Use demopanel1 to create a layout. @@ -9,17 +8,13 @@ % (f) Export using smoothing. % (g) Export to EPS, rather than PNG. - - %% (a) % delegate -demopanel1 +demopanel1; % see "help panel/export" for the full range of options. - - %% (b) % the default sizing model for export targets a piece of @@ -37,8 +32,6 @@ % that we did not supply a file extension, so PNG format is % assumed. - - %% (c) % one thing you might want to vary from figure to @@ -60,8 +53,6 @@ % specify width and height directly. see "help % panel/export". - - %% (d) % when you're done drafting your document, you can bring up @@ -69,15 +60,13 @@ % means "publication resolution" (600DPI). p.export('export_d', '-a1.4', '-rp'); - - %% (e) % once exported at final resolution, i can't help % noticing the margins are a little generous. let's pull % them in as tight as we dare to reduce the whitespace. p.de.margin = 1; -p(1,1).marginbottom = 9; +p(1, 1).marginbottom = 9; p(2).marginleft = 12; p.margin = [10 8 0.5 0.5]; p.export('export_e', '-a1.4', '-rp'); @@ -87,8 +76,6 @@ % layout between the on-screen renderer, the PNG renderer, % and the EPS renderer. - - %% (f) % that's now exported at 600DPI, which is fine for most @@ -110,8 +97,6 @@ % anti-aliasing. nonetheless, i find the results can be % useful. - - %% (g) % export by default is to PNG format - other formats are @@ -123,6 +108,3 @@ % NB: if you try to export to "svg" format, panel will use % plot2svg() if it is present. if not, you can find it at % file exchange (http://goo.gl/VzHIR at time of writing). - - - diff --git a/lib/panel-2.14/demo/demopanelB.m b/lib/panel-2.14/demo/demopanelB.m index 24f2183d..42695e32 100644 --- a/lib/panel-2.14/demo/demopanelB.m +++ b/lib/panel-2.14/demo/demopanelB.m @@ -1,4 +1,3 @@ - % Panel can incorporate an existing axis. % % (a) Create the root panel. @@ -6,8 +5,6 @@ % (c) Pack an automatically created axis, and your own axis, % into the root panel. - - %% (a) % create a column-pair layout, with 95% of the space given @@ -19,10 +16,8 @@ h_axis = p(1).select(); % and, hell, an image too -[X,Y,Z] = peaks(50); -surfc(X,Y,Z); - - +[X, Y, Z] = peaks(50); +surfc(X, Y, Z); %% (b) @@ -31,12 +26,7 @@ % colorbar... h_colorbar_axis = colorbar('peer', h_axis); - - %% (c) % panel can manage the layout of these too p(2).select(h_colorbar_axis); - - - diff --git a/lib/panel-2.14/demo/demopanelC.m b/lib/panel-2.14/demo/demopanelC.m index 3a5f19ec..2b104fc5 100644 --- a/lib/panel-2.14/demo/demopanelC.m +++ b/lib/panel-2.14/demo/demopanelC.m @@ -1,23 +1,18 @@ - % Recovering a Panel from a Figure. % % (a) Create a grid of panels, and show something in them. % (b) Recover the root panel from the Figure. - - %% (a) % create a 2x2 grid in gcf. -clf +clf; p = panel(); p.pack(2, 2); % show dummy content p.select('data'); - - %% (b) % say we returned from a function and didn't have a handle @@ -29,6 +24,5 @@ % note that "p" and "q" now refer to the same thing - it's % not two root panels, it's two references to the same one. if p == q - disp('panels are identical') + disp('panels are identical'); end - diff --git a/lib/panel-2.14/demo/demopanelD.m b/lib/panel-2.14/demo/demopanelD.m index d4d27beb..04481ee6 100644 --- a/lib/panel-2.14/demo/demopanelD.m +++ b/lib/panel-2.14/demo/demopanelD.m @@ -1,4 +1,3 @@ - % Panel can be child or parent to any graphics object. % % (a) Create a figure a uipanel. @@ -6,28 +5,21 @@ % (c) Select another uipanel into one of the sub-panels. % (d) Attach a callback. - - %% (a) % create the figure -clf +clf; % create a uipanel set(gcf, 'units', 'normalized'); u1 = uipanel('units', 'normalized', 'position', [0.1 0.1 0.8 0.8]); - - %% (b) % create a 2x3 grid in one of the uipanels p = panel(u1); p.pack(2, 3); - - - %% (c) % create another uipanel @@ -37,10 +29,7 @@ p(2, 2).select(u2); % select all other panels in the grid as axes -p.select('data') - - - +p.select('data'); %% (d) @@ -50,6 +39,3 @@ % function handle. someUserData = struct('whether_a_donkey_is_a_marine_mammal', false); p(2, 2).addCallback(@demopanel_callback, someUserData); - - - diff --git a/lib/panel-2.14/demo/demopanelE.m b/lib/panel-2.14/demo/demopanelE.m index 06891bf7..6a4b79ff 100644 --- a/lib/panel-2.14/demo/demopanelE.m +++ b/lib/panel-2.14/demo/demopanelE.m @@ -1,24 +1,19 @@ - % You can have as many root Panels as you like in one Figure. % % (a) Create a figure with two uipanel objects. % (b) Attach a panel to one of these. % (c) Attach another - oh, wait! - - %% (a) % create the figure -clf +clf; % create a couple of uipanels set(gcf, 'units', 'normalized'); u1 = uipanel('units', 'normalized', 'position', [0.1 0.1 0.35 0.8]); u2 = uipanel('units', 'normalized', 'position', [0.55 0.1 0.35 0.8]); - - %% (b) % create a 2x2 grid in one of the uipanels @@ -27,19 +22,17 @@ p.select('all'); % see? -pause(3) - - +pause(3); %% (c) - + % and, what the hell, another in the other q = panel(u2); q.pack(2, 2); q.select('all'); % oh, wait, the first one's disappeared. why? -pause(3) +pause(3); % by default, only one panel can be attached to any one % figure - if an existing panel is attached when you create @@ -52,7 +45,7 @@ p.select('all'); % see? -pause(3) +pause(3); % and, of course, if we try to create a new one again, once % again without 'add', we'll delete all existing panels, as @@ -62,12 +55,9 @@ p.select('all'); % see? -pause(3) +pause(3); % finally, let's show how to delete the first one, just for % the craic. you shouldn't usually need to do this, but it % works just fine. delete(p); - - - diff --git a/lib/panel-2.14/demo/demopanelF.m b/lib/panel-2.14/demo/demopanelF.m index 6726b4e0..9a2bf10a 100644 --- a/lib/panel-2.14/demo/demopanelF.m +++ b/lib/panel-2.14/demo/demopanelF.m @@ -1,4 +1,3 @@ - % You can manage fonts yourself, if you prefer. % % Panel, by default, manages fonts for all managed objects, @@ -10,39 +9,35 @@ % (a) Manage fonts globally (default). % (b) Do not manage fonts. - - %% (a) % create -figure(1) -clf +figure(1); +clf; p = panel(); p.pack(2, 2); hh = p.select('all'); % create xlabels for h = hh - xlabel(h, 'this will render as Arial', 'fontname', 'times'); + xlabel(h, 'this will render as Arial', 'fontname', 'times'); end % manage fonts globally p.fontname = 'Arial'; - - %% (b) % create -figure(2) -clf +figure(2); +clf; q = panel('no-manage-font'); q.pack(2, 2); hh = q.select('all'); % create xlabels for h = hh - xlabel(h, 'this will render as Times', 'fontname', 'times'); + xlabel(h, 'this will render as Times', 'fontname', 'times'); end % attempt to manage fonts globally (no effect) diff --git a/lib/panel-2.14/demo/demopanelG.m b/lib/panel-2.14/demo/demopanelG.m index 3a8892c2..b606e99f 100644 --- a/lib/panel-2.14/demo/demopanelG.m +++ b/lib/panel-2.14/demo/demopanelG.m @@ -1,4 +1,3 @@ - % One panel can manage multiple axes/graphics objects. % % 19/07/12 This example, and the multi-object functionality, @@ -9,21 +8,17 @@ % (b) Create two user axes. % (c) Have them both managed by a panel. - - %% (a) % create -clf +clf; p = panel(); p.pack(2, 2); % select sample data into some of them -p(1,1).select('data'); -p(1,2).select('data'); -p(2,1).select('data'); - - +p(1, 1).select('data'); +p(1, 2).select('data'); +p(2, 1).select('data'); %% (b) @@ -34,7 +29,7 @@ ax1 = axes(); % transparent axis for extra axis labelling -ax2 = axes('Color', 'none', 'XAxisLocation', 'top','YAxisLocation', 'Right'); +ax2 = axes('Color', 'none', 'XAxisLocation', 'top', 'YAxisLocation', 'Right'); % set up the fancy labelling (due to Brendan) OppTickLabels = {'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k'}; @@ -42,9 +37,7 @@ set(ax2, 'XTick', get(ax1, 'XTick'), 'YTick', get(ax1, 'YTick')); set(ax2, 'XTickLabel', OppTickLabels, 'YTickLabel', OppTickLabels); - - %% (c) % hand both axes to panel for position management -p(2,2).select([ax1 ax2]); +p(2, 2).select([ax1 ax2]); diff --git a/lib/panel-2.14/demo/demopanelH.m b/lib/panel-2.14/demo/demopanelH.m index 66454cd7..2f82622f 100644 --- a/lib/panel-2.14/demo/demopanelH.m +++ b/lib/panel-2.14/demo/demopanelH.m @@ -1,4 +1,3 @@ - % You can create an "inset" plot effect. % % 20/09/12 This example was inspired by the Matlab Central @@ -9,11 +8,10 @@ % (a) Create the layout. % (b) Display some data for illustration. - %% (a) % create a row of 2 panels (packed relative and horizontal) -clf +clf; p = panel(); p.pack('h', 2); @@ -25,8 +23,6 @@ % parent panel of p(2, 1) (the main plot) and p(2, 2) (the % inset). - - %% (b) % select sample data into all @@ -34,5 +30,3 @@ % tidy up set(p(2, 2).axis, 'xtick', [], 'ytick', []); - - diff --git a/lib/panel-2.14/demo/demopanelI.m b/lib/panel-2.14/demo/demopanelI.m index 7b274b48..ae95cf5f 100644 --- a/lib/panel-2.14/demo/demopanelI.m +++ b/lib/panel-2.14/demo/demopanelI.m @@ -1,4 +1,3 @@ - % Panel can fix dotted/dashed lines on export. % % NB: Matlab's difficulty with dotted/dashed lines on export @@ -31,24 +30,19 @@ % * Currently does something a bit dumb with log plots. I % should really fix that... - - %% (a) % create a column of 2 panels (packed relative) -clf +clf; p = panel(); p.pack(2); p.margin = [10 10 2 10]; p.de.margin = 15; - - - %% (b/c) % create a circle -th = linspace(0, 2*pi, 13); +th = linspace(0, 2 * pi, 13); x = cos(th) * 0.4 + 0.5; y = sin(th) * 0.4 + 0.5; mt = '.'; @@ -58,41 +52,36 @@ % for each for pind = 1:2 - % plot - p(pind).select(); - plot(x, y, 'k-'); - hold on - plot(x+1, y, 'r--'); - plot(x+2, y, 'g-.'); - plot(x+3, y, 'b:'); - plot(x, y+1, ['k' mt '-'], 'markersize', ms); - plot(x+1, y+1, ['r' mt '--'], 'markersize', ms); - plot(x+2, y+1, ['g' mt '-.'], 'markersize', ms); - plot(x+3, y+1, ['b' mt ':'], 'markersize', ms); - - % finalise - set(allchild(gca), 'linewidth', lw); - axis([0 5 0 2]); - - % legend - h_leg = legend('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'); - - % finalise - if pind == 2 - title('with fixdash()'); - p.fixdash([allchild(gca); allchild(h_leg)]); - else - title('without fixdash()'); - end + % plot + p(pind).select(); + plot(x, y, 'k-'); + hold on; + plot(x + 1, y, 'r--'); + plot(x + 2, y, 'g-.'); + plot(x + 3, y, 'b:'); + plot(x, y + 1, ['k' mt '-'], 'markersize', ms); + plot(x + 1, y + 1, ['r' mt '--'], 'markersize', ms); + plot(x + 2, y + 1, ['g' mt '-.'], 'markersize', ms); + plot(x + 3, y + 1, ['b' mt ':'], 'markersize', ms); + + % finalise + set(allchild(gca), 'linewidth', lw); + axis([0 5 0 2]); + + % legend + h_leg = legend('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'); + + % finalise + if pind == 2 + title('with fixdash()'); + p.fixdash([allchild(gca); allchild(h_leg)]); + else + title('without fixdash()'); + end end - - %% (d) % export p.export('demopanelI.png', '-w120', '-h120', '-rp'); - - - diff --git a/lib/panel-2.14/demo/demopanelJ.m b/lib/panel-2.14/demo/demopanelJ.m index 9b1ac4d9..6ff39288 100644 --- a/lib/panel-2.14/demo/demopanelJ.m +++ b/lib/panel-2.14/demo/demopanelJ.m @@ -1,4 +1,3 @@ - % Panels can have fixed physical size. % % Panels usually have a size which is a fraction of the size @@ -10,8 +9,6 @@ % (a) Create layout with one panel of fixed physical size. % (b) Show how units affect behaviour. - - %% (a) % create a column of 2 panels (packed relative) but with @@ -19,13 +16,11 @@ % putting the value inside {a cell array}, as in {25}, % below. it's 25mm because the current units of p are mm (mm % are the default unit). -clf +clf; p = panel(); p.pack({{25} []}); p.select('data'); - - %% (b) % but we can change the units. @@ -35,5 +30,3 @@ % we've chosen. this is hardly a resize - this changes it % from 25mm to 25.4mm. p(1).repack({1}); - - diff --git a/lib/panel-2.14/demo/demopanelK.m b/lib/panel-2.14/demo/demopanelK.m index 2fd8a3dd..078fbd4b 100644 --- a/lib/panel-2.14/demo/demopanelK.m +++ b/lib/panel-2.14/demo/demopanelK.m @@ -1,4 +1,3 @@ - % Compare performance between Panel and subplot. % % If you want to see whether Panel is slow or fast on your @@ -9,95 +8,81 @@ % (ii) Plot into each sub-panel. % (b) Compare performance. - - % prepare for performance testing -close all -ss = get(0,'Screensize'); -pp = [ss(3:4)/2 + [-599 -399] 1200 800]; -figure(1) -set(gcf, 'Position', pp) -figure(2) -set(gcf, 'Position', pp) -drawnow +close all; +ss = get(0, 'Screensize'); +pp = [ss(3:4) / 2 + [-599 -399] 1200 800]; +figure(1); +set(gcf, 'Position', pp); +figure(2); +set(gcf, 'Position', pp); +drawnow; N = 6; -tic +tic; % optional stuff optional = true; - - %% (a) For each approach: for approach = [1 2] - - % select figure - figure(approach) - - % performance - ti(approach) = toc; - - - - %% (i) - - % create a NxN grid in gcf. this is only necessary for - % panel - it is done implicitly when using subplot. - if approach == 1 - p = panel(); - p.pack(N, N); - end - - - - %% (ii) - - % plot into each panel in turn - - for m = 1:N - for n = 1:N - - % select one of the NxN grid of sub-panels - if approach == 1 - p(m, n).select(); - else - subplot(N, N, m + (n-1) * N); - end - - % optional, do some stuff - if optional - - % plot some data - plot(randn(100,1)); - - % you can use all the usual calls - xlabel('sample number'); - ylabel('data'); - - % and so on - generally, you can treat the axis panel - % like any other axis - axis([0 100 -3 3]); - - end - - end - end - - % performance - drawnow - tf(approach) = toc; - - - -end + % select figure + figure(approach); + + % performance + ti(approach) = toc; + + %% (i) + + % create a NxN grid in gcf. this is only necessary for + % panel - it is done implicitly when using subplot. + if approach == 1 + p = panel(); + p.pack(N, N); + end + + %% (ii) + + % plot into each panel in turn + + for m = 1:N + for n = 1:N + + % select one of the NxN grid of sub-panels + if approach == 1 + p(m, n).select(); + else + subplot(N, N, m + (n - 1) * N); + end + + % optional, do some stuff + if optional + + % plot some data + plot(randn(100, 1)); + + % you can use all the usual calls + xlabel('sample number'); + ylabel('data'); + + % and so on - generally, you can treat the axis panel + % like any other axis + axis([0 100 -3 3]); + + end + + end + end + + % performance + drawnow; + tf(approach) = toc; + +end %% (b) measure performance td = tf - ti; fprintf('Time taken using panel: %.3f s\n', td(1)); fprintf('Time taken using subplot: %.3f s\n', td(2)); - - - diff --git a/lib/panel-2.14/demo/demopanel_callback.m b/lib/panel-2.14/demo/demopanel_callback.m index 585fbcee..aff3edfe 100644 --- a/lib/panel-2.14/demo/demopanel_callback.m +++ b/lib/panel-2.14/demo/demopanel_callback.m @@ -1,25 +1,21 @@ - % this callback is attached by demopanelD function demopanel_callback(data) -disp('---- ENTER CALLBACK ----') - -% all the information is in this structure. -data -context = data.context -userdata = data.userdata - -% the "context" field provides rendering data, particularly -% the "size_in_mm" is the size of the rendering surface (the -% figure window, or an image file) whilst the "rect" is the -% rectangle assigned to this panel. therefore, we can work -% out the rendered (physical) size of this panel (and -% therefore, usually, the object it manages) with the -% following calculation. -size = data.context.size_in_mm .* data.context.rect(3:4) - -disp('---- EXIT CALLBACK ----') + disp('---- ENTER CALLBACK ----'); + % all the information is in this structure. + data; + context = data.context; + userdata = data.userdata; + % the "context" field provides rendering data, particularly + % the "size_in_mm" is the size of the rendering surface (the + % figure window, or an image file) whilst the "rect" is the + % rectangle assigned to this panel. therefore, we can work + % out the rendered (physical) size of this panel (and + % therefore, usually, the object it manages) with the + % following calculation. + size = data.context.size_in_mm .* data.context.rect(3:4); + disp('---- EXIT CALLBACK ----'); diff --git a/lib/panel-2.14/demo/demopanel_minihist.m b/lib/panel-2.14/demo/demopanel_minihist.m index 7dfc507b..2e741501 100644 --- a/lib/panel-2.14/demo/demopanel_minihist.m +++ b/lib/panel-2.14/demo/demopanel_minihist.m @@ -1,80 +1,69 @@ - % this function is used by some of the demos to display data function demopanel_minihist(stats, show_xtick, show_ytick) -% color -col = histcol(stats.source); - -% plot -b = bar(stats.bincens, stats.percfreq, 0.9); -set(b, 'facecolor', palecol(col), 'edgecolor', col, 'showbaseline', 'off'); -hold on - -% mean -x = mean(stats.values) * [1 1]; -y = [0 100]; -plot(x, y, 'k-', 'linewidth', 1); - -% label -set(gca, 'ytick', stats.ytick); -if ~show_ytick - set(gca, 'yticklabel', {}); -end - -% label -set(gca, 'xtick', stats.xtick); -if ~show_xtick - set(gca, 'xticklabel', {}); -end - -% finalise axis -axis([stats.binrange 0 stats.percpeak]); -grid on - -% overflows -N = sum(stats.values > max(stats.binrange)); -if N - y = stats.percpeak * 0.8; - x = stats.binrange(1) + [0.98] * diff(stats.binrange); - text(x, y, [int2str(N) '>'], 'hori', 'right', 'fontsize', 8); -end + % color + col = histcol(stats.source); + + % plot + b = bar(stats.bincens, stats.percfreq, 0.9); + set(b, 'facecolor', palecol(col), 'edgecolor', col, 'showbaseline', 'off'); + hold on; + + % mean + x = mean(stats.values) * [1 1]; + y = [0 100]; + plot(x, y, 'k-', 'linewidth', 1); + + % label + set(gca, 'ytick', stats.ytick); + if ~show_ytick + set(gca, 'yticklabel', {}); + end + + % label + set(gca, 'xtick', stats.xtick); + if ~show_xtick + set(gca, 'xticklabel', {}); + end + + % finalise axis + axis([stats.binrange 0 stats.percpeak]); + grid on; + + % overflows + N = sum(stats.values > max(stats.binrange)); + if N + y = stats.percpeak * 0.8; + x = stats.binrange(1) + [0.98] * diff(stats.binrange); + text(x, y, [int2str(N) '>'], 'hori', 'right', 'fontsize', 8); + end + + % overflows + N = sum(stats.values < min(stats.binrange)); + if N + y = stats.percpeak * 0.8; + x = stats.binrange(1) + [0.02] * diff(stats.binrange); + text(x, y, ['<' int2str(N)], 'hori', 'left', 'fontsize', 8); + end -% overflows -N = sum(stats.values < min(stats.binrange)); -if N - y = stats.percpeak * 0.8; - x = stats.binrange(1) + [0.02] * diff(stats.binrange); - text(x, y, ['<' int2str(N)], 'hori', 'left', 'fontsize', 8); -end - - - - - function col = histcol(source) -switch source - - case 'X' - col = [1 0 0]; - - case 'Y' - col = [0 0.5 0]; - - case 'Z' - col = [0 0 1]; - -end + switch source + case 'X' + col = [1 0 0]; + case 'Y' + col = [0 0.5 0]; - + case 'Z' + col = [0 0 1]; -function c = palecol(c) - -t = [1 1 1]; -d = t - c; -c = c + (d * 0.5); + end +function c = palecol(c) + t = [1 1 1]; + d = t - c; + c = c + (d * 0.5); diff --git a/lib/panel-2.14/panel.m b/lib/panel-2.14/panel.m index 92b0c070..a084a54c 100644 --- a/lib/panel-2.14/panel.m +++ b/lib/panel-2.14/panel.m @@ -1,9 +1,8 @@ - % Panel is an alternative to Matlab's "subplot" function. -% +% % INSTALLATION. To install panel, place the file "panel.m" % on your Matlab path. -% +% % DOCUMENTATION. Scan the introductory information in the % folder "docs". Learn to use panel by working through the % demonstration scripts in the folder "demo" (list the demos @@ -11,20 +10,18 @@ % available through "doc panel" or "help panel". For the % change log, use "edit panel" to view the file "panel.m". - - % CHANGE LOG -% +% % ############################################################ % 22/05/2011 % First Public Release Version 2.0 % ############################################################ -% +% % 23/05/2011 % Incorporated an LP solver, since the one we were using % "linprog()" is not available to users who do not have the % Optimisation Toolbox installed. -% +% % 21/06/2011 % Added -opdf option, and changed PageSize to be equal to % PaperPosition. @@ -315,4887 +312,4773 @@ % ############################################################ classdef (Sealed = true) panel < handle - - - - %% ---- PROPERTIES ---- - - properties (Constant = true, Hidden = true) - - PANEL_TYPE_UNCOMMITTED = 0; - PANEL_TYPE_PARENT = 1; - PANEL_TYPE_OBJECT = 2; - - end - - properties (Constant = true) - - LAYOUT_MODE_NORMAL = 0; - LAYOUT_MODE_PREPRINT = 1; - LAYOUT_MODE_POSTPRINT = 2; - - end - - properties - - % these properties are only here for documentation. they - % are actually stored in "prop". it's just some subsref - % madness. - - % font name to use for axis text (inherited) - % - % access: read/write - % default: normal - fontname - - % font size to use for axis text (inherited) - % - % access: read/write - % default: normal - fontsize - - % font weight to use for axis text (inherited) - % - % access: read/write - % default: normal - fontweight - - % the units that are used when reading/writing margins - % - % units can be set to any of 'mm', 'cm', 'in' or 'pt'. - % it only affects the read/write interface; values - % stored already are not re-interpreted. - % - % access: read/write - % default: mm - units - - % the panel's margin vector in the form [left bottom right top] - % - % the margin is key to the layout process. the layout - % algorithm makes all panels as large as possible whilst - % not violating margin constraints. margins are - % respected between panels within their parent and - % between the root panel and the edges of the canvas - % (figure or image file). - % - % access: read/write - % default: [12 10 2 2] (mm) - % - % see also: marginleft, marginbottom, marginright, margintop - margin - - % one element of the margin vector - % - % access: read/write - % default: see margin - % - % see also: margin - marginleft - - % one element of the margin vector - % - % access: read/write - % default: see margin - % - % see also: margin - marginbottom - - % one element of the margin vector - % - % access: read/write - % default: see margin - % - % see also: margin - marginright - - % one element of the margin vector - % - % access: read/write - % default: see margin - % - % see also: margin - margintop - - % return position of panel - % - % return the panel's position in normalized - % coordinates (normalized to the figure window that - % is associated with the panel). note that parent - % panels have positions too, even though nothing is - % usually rendered. uncommitted panels, too. - % - % access: read only - position - - % return handle of associated figure - % - % access: read only - figure - - % return handle of associated axis - % - % if the panel is not an axis panel, empty is returned. - % object includes axis, but axis does not include - % object. - % - % access: read only - % - % see also: object - axis - - % return handle of associated object - % - % if the panel is not an object panel, empty is - % returned. object includes axis, but axis does not - % include object. - % - % access: read only - % - % see also: axis - object - - % access properties of panel's children - % - % if the panel is a parent panel, "children" gives - % access to some properties of its children (direct - % descendants). "children" can be abbreviated "ch". - % properties that can be accessed are as follows. - % - % axis: read-only, returns an array - % object: read-only, returns an array - % - % margin: write-only - % fontname: write-only - % fontsize: write-only - % fontweight: write-only - % - % EXAMPLE: - % h = p.ch.axis; - % p.ch.margin = 3; - % - % see also: descendants, family - children - - % access properties of panel's descendants - % - % if the panel is a parent panel, "descendants" gives - % access to some properties of its descendants - % (children, grandchildren, etc.). "descendants" can be - % abbreviated "de". properties that can be accessed are - % as follows. - % - % axis: read-only, returns an array - % object: read-only, returns an array - % - % margin: write-only - % fontname: write-only - % fontsize: write-only - % fontweight: write-only - % - % EXAMPLE: - % h = p.de.axis; - % p.de.margin = 3; - % - % see also: children, family - descendants - - % access properties of panel's family - % - % if the panel is a parent panel, "family" gives access - % to some properties of its family (self, children, - % grandchildren, etc.). "family" can be abbreviated - % "fa". properties that can be accessed are as follows. - % - % axis: read-only, returns an array - % object: read-only, returns an array - % - % margin: write-only - % fontname: write-only - % fontsize: write-only - % fontweight: write-only - % - % EXAMPLE: - % h = p.fa.axis; - % p.fa.margin = 3; - % - % see also: children, descendants - family - - end - - properties (Access = private) - - % associated figure window - h_figure - - % parent graphics object - h_parent - - % this is empty for the root PANEL, populated for all others - parent - - % this is always the root panel associated with this - m_root - - % packing specifier - % - % empty: relative positioning mode (stretch) - % scalar fraction: relative positioning mode - % scalar percentage: relative positioning mode - % 1x4 dimension: absolute positioning mode - packspec - - % packing dimension of children - % - % 1 : horizontal - % 2 : vertical - packdim - - % panel type - m_panelType - - % fixdash lines - m_fixdash - m_fixdash_restore - - % associated managed graphics object (usually, an axis) - h_object - - % show axis (only the root has this extra axis, if show() is active) - h_showAxis - - % children (only a parent panel has non-empty, here) - m_children - - % callback (any functions listed in this cell array are called when events occur) - m_callback - - % local properties (actual properties is this overlaid on inherited/default properties) - % - % see getPropertyValue() - prop - - % state - % - % private state information used during various processing - state - - % layout context for this panel - % - % this is the layout context for the panel. this is - % computed in the function recomputeLayout(), and used - % to reposition the panel in applyLayout(). storage of - % this data means that we can call applyLayout() to - % layout only a branch of the panel tree without having - % to recompute the whole thing. however, I don't know - % how efficient this system is, might need some work. - m_context - - end - - - - - - - - %% ---- PUBLIC CTOR/DTOR ---- - - methods - - function p = panel(varargin) - - % create a new panel - % - % p = panel(...) - % create a new root panel. optional arguments listed - % below can be supplied in any order. if "h_parent" - % is not supplied, it is set to gcf - that is, the - % panel fills the current figure. - % - % initially, the root panel is an "uncommitted - % panel". calling pack() or select() on it will - % commit it as a "parent panel" or an "object - % panel", respectively. the following arguments may - % be passed, in any order. - % - % h_parent - % a handle to a graphics object that will act as the - % parent of the new panel. this is usually a figure - % handle, but may be a handle to any graphics - % object, in principle. currently, an error is - % raised unless it's a figure or a uipanel - if you - % want to try other object types, edit the code - % where the error is raised, and let me know if you - % have positive results so I can update panel to - % allow other object types. - % - % 'add' - % usually, when you attach a new root panel to a - % figure, any existing attached root panels are - % first deleted to make way for it. if you pass this - % argument, this is not done, so that you can attach - % more than one root panel to the same figure. see - % demopanelE for an example of this use. - % - % 'no-manage-font' - % by default, a panel will manage fonts of titles - % and axis labels. this prevents the user from - % setting individual fonts on those items. pass this - % flag to disable font management for this panel. - % - % 'mm', 'cm', 'in', 'pt' - % by default, panel uses mm as the unit of - % communication with the user over margin sizes. - % pass any of these to change this (you can achieve - % the same effect after creating a panel by setting - % the property "units"). - % - % see also: panel (overview), pack(), select() - - % PRIVATE DOCUMENTATION - % - % 'defer' - % THIS IS NO LONGER ADVERTISED since we replaced the - % LP solution with a procedural solution, but still - % functions as before, to provide legacy support. - % the panel will be created with layout disabled. - % the layout computations take a little while when - % large numbers of panels are involved, and are - % re-run every time you add a panel or change a - % margin, by default. this is tedious if you are - % preparing a complex layout; pass 'defer', and - % layout will not be computed at all until you call - % refresh() or export() on the root panel. - % - % 'pack' - % this constructor is called internally from pack() - % to create new panels when packing them into - % parents. the first argument is passed as 'pack' in - % this case, which allows us to do slightly quicker - % parsing of the arguments, since we know the - % calling convention (see immediately below). - - % default state - p.state = []; - p.state.name = ''; - p.state.defer = 0; - p.state.manage_font = 1; - p.m_callback = {}; - p.m_fixdash = {}; - p.packspec = []; - p.packdim = 2; - p.m_panelType = p.PANEL_TYPE_UNCOMMITTED; - p.prop = panel.getPropertyInitialState(); - - % handle call from pack() aqap - if nargin && isequal(varargin{1}, 'pack') - - % since we know the calling convention, in this - % case, we can handle this as quickly as possible, - % so that large recursive layouts do not get held up - % by spurious code, here. - - % parent is a panel - passed_h_parent = varargin{2}; - - % become its child - indexInParent = int2str(length(passed_h_parent.m_children)+1); - if passed_h_parent.isRoot() - p.state.name = ['(' indexInParent ')']; - else - p.state.name = [passed_h_parent.state.name(1:end-1) ',' indexInParent ')']; - end - p.h_parent = passed_h_parent.h_parent; - p.h_figure = passed_h_parent.h_figure; - p.parent = passed_h_parent; - p.m_root = passed_h_parent.m_root; - - % done! - return - - end - - % default condition - passed_h_parent = []; - add = false; - - % peel off args - while ~isempty(varargin) - - % get arg - arg = varargin{1}; - varargin = varargin(2:end); - - % handle text - if ischar(arg) - - switch arg - - case 'add' - add = true; - continue; - - case 'defer' - p.state.defer = 1; - continue; - - case 'no-manage-font' - p.state.manage_font = 0; - continue; - - case {'mm' 'cm' 'in' 'pt'} - p.setPropertyValue('units', arg); - continue; - - otherwise - error('panel:InvalidArgument', ['unrecognised text argument "' arg '"']); - - end - - end - - % handle parent - if isscalar(arg) && ishandle(arg) - passed_h_parent = arg; - continue; - end - - % error - error('panel:InvalidArgument', 'unrecognised argument to panel constructor'); - - end - - % attach to current figure if no parent supplied - if isempty(passed_h_parent) - passed_h_parent = gcf; - - % this might cause a figure to be created - if so, - % give it time to display now so we don't get a (or - % two, in fact!) resize event(s) later - drawnow - end - - % we are a root panel - p.state.name = 'root'; - p.parent = []; - p.m_root = p; - - % get parent type - parentType = get(passed_h_parent, 'type'); - - % set handles - switch parentType - - case 'uipanel' - p.h_parent = passed_h_parent; - p.h_figure = getParentFigure(passed_h_parent); - - case 'figure' - p.h_parent = passed_h_parent; - p.h_figure = passed_h_parent; - - otherwise - error('panel:InvalidArgument', ... - ['panel() cannot be attached to an object of type "' parentType '"']); - - end - - % lay in callbacks - addHandleCallback(p.h_figure, 'CloseRequestFcn', @panel.closeCallback); - addHandleCallback(p.h_parent, 'ResizeFcn', @panel.resizeCallback); - - % register for callbacks - if add - panel.callbackDispatcher('registerNoClear', p); - else - panel.callbackDispatcher('register', p); - end - - % lock class in memory (prevent persistent from being cleared by clear all) - panel.lockClass(); - - end - - function delete(p) - - % destroy a panel - % - % delete(p) - % destroy the passed panel, deleting all associated - % graphics objects. - % - % NB: you won't usually have to call this explicitly. - % it is called automatically for all attached panels - % when you close the associated figure. - - % debug output -% panel.debugmsg(['deleting "' p.state.name '"...']); - - % delete managed graphics objects - for n = 1:length(p.h_object) - h = p.h_object(n); - if ishandle(h) - delete(h); - end - end - - % delete associated show axis - if ~isempty(p.h_showAxis) && ishandle(p.h_showAxis) - delete(p.h_showAxis); - end - - % delete all children (child will remove itself from - % "m_children" on delete()) - while ~isempty(p.m_children) - delete(p.m_children(end)); - end - - % unregister... - if p.isRoot() - - % ...for callbacks - panel.callbackDispatcher('unregister', p); - - else - - % ...from parent - p.parent.removeChild(p); - - end - - % debug output -% panel.debugmsg(['deleted "' p.state.name '"!']); - - end - - end - - - - - - - - - - - - - - - %% ---- PUBLIC DISPLAY ---- - - methods (Hidden = true) - - function disp(p) - - display(p); - + + %% ---- PROPERTIES ---- + + properties (Constant = true, Hidden = true) + + PANEL_TYPE_UNCOMMITTED = 0 + PANEL_TYPE_PARENT = 1 + PANEL_TYPE_OBJECT = 2 + + end + + properties (Constant = true) + + LAYOUT_MODE_NORMAL = 0 + LAYOUT_MODE_PREPRINT = 1 + LAYOUT_MODE_POSTPRINT = 2 + + end + + properties + + % these properties are only here for documentation. they + % are actually stored in "prop". it's just some subsref + % madness. + + % font name to use for axis text (inherited) + % + % access: read/write + % default: normal + fontname + + % font size to use for axis text (inherited) + % + % access: read/write + % default: normal + fontsize + + % font weight to use for axis text (inherited) + % + % access: read/write + % default: normal + fontweight + + % the units that are used when reading/writing margins + % + % units can be set to any of 'mm', 'cm', 'in' or 'pt'. + % it only affects the read/write interface; values + % stored already are not re-interpreted. + % + % access: read/write + % default: mm + units + + % the panel's margin vector in the form [left bottom right top] + % + % the margin is key to the layout process. the layout + % algorithm makes all panels as large as possible whilst + % not violating margin constraints. margins are + % respected between panels within their parent and + % between the root panel and the edges of the canvas + % (figure or image file). + % + % access: read/write + % default: [12 10 2 2] (mm) + % + % see also: marginleft, marginbottom, marginright, margintop + margin + + % one element of the margin vector + % + % access: read/write + % default: see margin + % + % see also: margin + marginleft + + % one element of the margin vector + % + % access: read/write + % default: see margin + % + % see also: margin + marginbottom + + % one element of the margin vector + % + % access: read/write + % default: see margin + % + % see also: margin + marginright + + % one element of the margin vector + % + % access: read/write + % default: see margin + % + % see also: margin + margintop + + % return position of panel + % + % return the panel's position in normalized + % coordinates (normalized to the figure window that + % is associated with the panel). note that parent + % panels have positions too, even though nothing is + % usually rendered. uncommitted panels, too. + % + % access: read only + position + + % return handle of associated figure + % + % access: read only + figure + + % return handle of associated axis + % + % if the panel is not an axis panel, empty is returned. + % object includes axis, but axis does not include + % object. + % + % access: read only + % + % see also: object + axis + + % return handle of associated object + % + % if the panel is not an object panel, empty is + % returned. object includes axis, but axis does not + % include object. + % + % access: read only + % + % see also: axis + object + + % access properties of panel's children + % + % if the panel is a parent panel, "children" gives + % access to some properties of its children (direct + % descendants). "children" can be abbreviated "ch". + % properties that can be accessed are as follows. + % + % axis: read-only, returns an array + % object: read-only, returns an array + % + % margin: write-only + % fontname: write-only + % fontsize: write-only + % fontweight: write-only + % + % EXAMPLE: + % h = p.ch.axis; + % p.ch.margin = 3; + % + % see also: descendants, family + children + + % access properties of panel's descendants + % + % if the panel is a parent panel, "descendants" gives + % access to some properties of its descendants + % (children, grandchildren, etc.). "descendants" can be + % abbreviated "de". properties that can be accessed are + % as follows. + % + % axis: read-only, returns an array + % object: read-only, returns an array + % + % margin: write-only + % fontname: write-only + % fontsize: write-only + % fontweight: write-only + % + % EXAMPLE: + % h = p.de.axis; + % p.de.margin = 3; + % + % see also: children, family + descendants + + % access properties of panel's family + % + % if the panel is a parent panel, "family" gives access + % to some properties of its family (self, children, + % grandchildren, etc.). "family" can be abbreviated + % "fa". properties that can be accessed are as follows. + % + % axis: read-only, returns an array + % object: read-only, returns an array + % + % margin: write-only + % fontname: write-only + % fontsize: write-only + % fontweight: write-only + % + % EXAMPLE: + % h = p.fa.axis; + % p.fa.margin = 3; + % + % see also: children, descendants + family + + end + + properties (Access = private) + + % associated figure window + h_figure + + % parent graphics object + h_parent + + % this is empty for the root PANEL, populated for all others + parent + + % this is always the root panel associated with this + m_root + + % packing specifier + % + % empty: relative positioning mode (stretch) + % scalar fraction: relative positioning mode + % scalar percentage: relative positioning mode + % 1x4 dimension: absolute positioning mode + packspec + + % packing dimension of children + % + % 1 : horizontal + % 2 : vertical + packdim + + % panel type + m_panelType + + % fixdash lines + m_fixdash + m_fixdash_restore + + % associated managed graphics object (usually, an axis) + h_object + + % show axis (only the root has this extra axis, if show() is active) + h_showAxis + + % children (only a parent panel has non-empty, here) + m_children + + % callback (any functions listed in this cell array are called when events occur) + m_callback + + % local properties (actual properties is this overlaid on inherited/default properties) + % + % see getPropertyValue() + prop + + % state + % + % private state information used during various processing + state + + % layout context for this panel + % + % this is the layout context for the panel. this is + % computed in the function recomputeLayout(), and used + % to reposition the panel in applyLayout(). storage of + % this data means that we can call applyLayout() to + % layout only a branch of the panel tree without having + % to recompute the whole thing. however, I don't know + % how efficient this system is, might need some work. + m_context + + end + + %% ---- PUBLIC CTOR/DTOR ---- + + methods + + function p = panel(varargin) + + % create a new panel + % + % p = panel(...) + % create a new root panel. optional arguments listed + % below can be supplied in any order. if "h_parent" + % is not supplied, it is set to gcf - that is, the + % panel fills the current figure. + % + % initially, the root panel is an "uncommitted + % panel". calling pack() or select() on it will + % commit it as a "parent panel" or an "object + % panel", respectively. the following arguments may + % be passed, in any order. + % + % h_parent + % a handle to a graphics object that will act as the + % parent of the new panel. this is usually a figure + % handle, but may be a handle to any graphics + % object, in principle. currently, an error is + % raised unless it's a figure or a uipanel - if you + % want to try other object types, edit the code + % where the error is raised, and let me know if you + % have positive results so I can update panel to + % allow other object types. + % + % 'add' + % usually, when you attach a new root panel to a + % figure, any existing attached root panels are + % first deleted to make way for it. if you pass this + % argument, this is not done, so that you can attach + % more than one root panel to the same figure. see + % demopanelE for an example of this use. + % + % 'no-manage-font' + % by default, a panel will manage fonts of titles + % and axis labels. this prevents the user from + % setting individual fonts on those items. pass this + % flag to disable font management for this panel. + % + % 'mm', 'cm', 'in', 'pt' + % by default, panel uses mm as the unit of + % communication with the user over margin sizes. + % pass any of these to change this (you can achieve + % the same effect after creating a panel by setting + % the property "units"). + % + % see also: panel (overview), pack(), select() + + % PRIVATE DOCUMENTATION + % + % 'defer' + % THIS IS NO LONGER ADVERTISED since we replaced the + % LP solution with a procedural solution, but still + % functions as before, to provide legacy support. + % the panel will be created with layout disabled. + % the layout computations take a little while when + % large numbers of panels are involved, and are + % re-run every time you add a panel or change a + % margin, by default. this is tedious if you are + % preparing a complex layout; pass 'defer', and + % layout will not be computed at all until you call + % refresh() or export() on the root panel. + % + % 'pack' + % this constructor is called internally from pack() + % to create new panels when packing them into + % parents. the first argument is passed as 'pack' in + % this case, which allows us to do slightly quicker + % parsing of the arguments, since we know the + % calling convention (see immediately below). + + % default state + p.state = []; + p.state.name = ''; + p.state.defer = 0; + p.state.manage_font = 1; + p.m_callback = {}; + p.m_fixdash = {}; + p.packspec = []; + p.packdim = 2; + p.m_panelType = p.PANEL_TYPE_UNCOMMITTED; + p.prop = panel.getPropertyInitialState(); + + % handle call from pack() aqap + if nargin && isequal(varargin{1}, 'pack') + + % since we know the calling convention, in this + % case, we can handle this as quickly as possible, + % so that large recursive layouts do not get held up + % by spurious code, here. + + % parent is a panel + passed_h_parent = varargin{2}; + + % become its child + indexInParent = int2str(length(passed_h_parent.m_children) + 1); + if passed_h_parent.isRoot() + p.state.name = ['(' indexInParent ')']; + else + p.state.name = [passed_h_parent.state.name(1:end - 1) ',' indexInParent ')']; end - - function display(p, label) - - if nargin == 2 - disp([10 label ' =' 10]) - end - - display_sub(p); - - disp(' ') - - end - - function display_sub(p, indent) - - % default - if nargin < 2 - indent = ''; - end - - % handle non-scalar (should not exist!) - nels = numel(p); - if nels > 1 - sz = size(p); - sz = sprintf('%dx', sz); - disp([sz(1:end-1) ' array of panel objects']); - return - end - - % header - header = indent; - if p.isObject() - header = [header 'Object ' p.state.name ': ']; - elseif p.isParent() - header = [header 'Parent ' p.state.name ': ']; - else - header = [header 'Uncommitted ' p.state.name ': ']; - end - if p.isRoot() - pp = ['attached to Figure ' panel.fignum(p.h_figure)]; - else - if isempty(p.packspec) - pp = 'stretch'; - elseif iscell(p.packspec) - units = p.getPropertyValue('units'); - val = panel.resolveUnits({p.packspec{1} 'mm'}, units); - pp = sprintf('%.1f%s', val, units); - elseif isscalar(p.packspec) - if p.packspec > 1 - pp = sprintf('%.0f%%', p.packspec); - else - pp = sprintf('%.3f', p.packspec); - end - else - pp = sprintf('%.3f ', p.packspec); - pp = pp(1:end-1); - end - end - header = [header '[' pp]; - if p.isParent() - edges = {'hori' 'vert'}; - header = [header ', ' edges{p.packdim}]; - end - header = [header ']']; - - % margin - header = rpad(header, 60); - header = [header '[ margin ' sprintf('%.3g ', p.getPropertyValue('margin')) p.getPropertyValue('units') ']']; - -% % index -% if isfield(p.state, 'index') -% header = [header ' (' int2str(p.state.index) ')']; -% end - - % display - disp(header); - - % children - for c = 1:length(p.m_children) - p.m_children(c).display_sub([indent ' ']); - end - - end - - end - - - - - - - - - - - %% ---- PUBLIC METHODS ---- - - methods - - function h = xlabel(p, text) - - % apply an xlabel to the panel (or group) - % - % p.xlabel(...) - % behaves just like xlabel() at the prompt (you can - % use that as an alternative) when called on an axis - % panel. when called on a parent panel, however, the - % group of objects within that parent have a label - % applied. when called on a non-axis object panel, - % an error is raised. - - h = get(p.getOrCreateAxis(), 'xlabel'); - set(h, 'string', text); - if p.isParent() - set(h, 'visible', 'on'); - end - - end - - function h = ylabel(p, text) - - % apply a ylabel to the panel (or group) - % - % p.ylabel(...) - % behaves just like ylabel() at the prompt (you can - % use that as an alternative) when called on an axis - % panel. when called on a parent panel, however, the - % group of objects within that parent have a label - % applied. when called on a non-axis object panel, - % an error is raised. - - h = get(p.getOrCreateAxis(), 'ylabel'); - set(h, 'string', text); - if p.isParent() - set(h, 'visible', 'on'); - end - - end - - function h = zlabel(p, text) - - % apply a zlabel to the panel (or group) - % - % p.zlabel(...) - % behaves just like zlabel() at the prompt (you can - % use that as an alternative) when called on an axis - % panel. when called on a parent panel, however, - % this method raises an error, since parent panels - % are assumed to be 2D, with respect to axes. - - if p.isParent() - error('panel:ZLabelOnParentAxis', 'can only call zlabel() on an object panel'); - end - - h = get(p.getOrCreateAxis(), 'zlabel'); - set(h, 'string', text); - - end - - function h = title(p, text) - - % apply a title to the panel (or group) - % - % p.title(...) - % behaves just like title() at the prompt (you can - % use that as an alternative) when called on an axis - % panel. when called on a parent panel, however, the - % group of objects within that parent have a title - % applied. when called on a non-axis object panel, - % an error is raised. - - h = title(p.getOrCreateAxis(), text); - if p.isParent() - set(h, 'visible', 'on'); - end - - end - - function hold(p, state) - - % set the hold on/off state of the associated axis - % - % p.hold('on' / 'off') - % you can use matlab's "hold" function with plots in - % panel, just like any other plot. there is, - % however, a very minor gotcha that is somewhat - % unlikely to ever come up, but for completeness - % this is the problem and the solutions: - % - % if you create a panel "p", change its font using - % panel, e.g. "p.fontname = 'Courier New'", then call - % "hold on", then "hold off", then plot into it, the - % font is not respected. this situation is unlikely to - % arise because there's usually no reason to call - % "hold off" on a plot. however, there are three - % solutions to get round it, if it does: - % - % a) call p.refresh() when you're finished, to - % update all fonts to managed values. - % - % b) if you're going to call p.export() anyway, - % fonts will get updated when you do. - % - % c) if for some reason you can't do (a) OR (b) (I - % can't think why), you can use the hold() function - % provided by panel instead of that provided by - % Matlab. this will not affect your fonts. for - % example, call "p(2).hold('on')". - - % because the matlab "hold off" command sets an axis's - % nextplot state to "replace", we lose control over - % the axis properties (such as fontname). we set - % nextplot to "replacechildren" when we create an - % axis, but if the user does a "hold on, hold off" - % cycle, we lose that. therefore, we offer this - % alternative. - - % check - if ~p.isObject() - error('panel:HoldWhenNotObjectPanel', 'can only call hold() on an object panel'); - end - - % check - h_axes = p.getAllManagedAxes(); - if isempty(h_axes) - error('panel:HoldWhenNoAxes', 'can only call hold() on a panel that manages one or more axes'); - end - - % switch - switch state - case {'on' true 1} - set(h_axes, 'nextplot', 'add'); - case {'off' false 0} - set(h_axes, 'nextplot', 'replacechildren'); - otherwise - error('panel:InvalidArgument', 'argument to hold() must be ''on'', ''off'', or boolean'); - end - - end - - function fixdash(p, hs, linestyle) - - % pass dashed lines to be fixed up during export - % - % NB: Matlab's difficulty with dotted/dashed lines on export - % seems to be fixed in R2014b, so if using this version or a - % later one, this functionality of panel will be of no - % interest. Text below was from pre R2014b. - % - % p.fixdash(h, linestyle) - % add the lines specified as handles in "h" to the - % list of lines to be "fixed up" during export. - % panel will attempt to get the lines to look right - % during export to all formats where they would - % usually get mussed up. see demopanelI for an - % example of how it works. - % - % the above is the usual usage of fixdash(), but - % you can get more control over linestyle by - % specifying the additional argument, "linestyle". - % if "linestyle" is supplied, it is used as the - % linestyle; if not, the current linestyle of the - % line (-, --, -., :) is used. "linestyle" can - % either be a text string or a series of numbers, as - % described below. - % - % '-' solid - % '--' dashed, equal to [2 0.75] - % '-.' dash-dot, equal to [2 0.75 0.5 0.75] - % ':', '.' dotted, equal to [0.5 0.5] - % - % a number series should be 1xN, where N is a - % multiple of 2, as in the examples above, and - % specifies the lengths of any number of dash - % components that are used before being repeated. - % for instance, '-.' generates a 2 unit segment - % (dash), a 0.75 unit gap, then a 0.5 unit segment - % (dot) and a final 0.75 unit gap. at present, the - % units are always millimetres. this system is - % extensible, so that the following examples are - % also valid: - % - % '--..' dash-dash-dot-dot - % '-..-.' dash-dot-dot-dash-dot - % [2 1 4 1 6 1] 2 dash, 4 dash, 6 dash - - % default - if nargin < 3 - linestyle = []; - end - - % bubble up to root - if ~p.isRoot() - p.m_root.fixdash(hs, linestyle); - return - end - - % for each passed handle - for h = (hs(:)') - - % check it's still a handle - if ~ishandle(h) - continue - end - - % check it's a line - if ~isequal(get(h, 'type'), 'line') - continue - end - - % update if in list - found = false; - for i = 1:length(p.m_fixdash) - if h == p.m_fixdash{i}.h - p.m_fixdash{i}.linestyle = linestyle; - found = true; - break - end - end - - % else add to list - if ~found - p.m_fixdash{end+1} = struct('h', h, 'linestyle', linestyle); - end - - end - - end - - function show(p) - - % highlight the outline of the panel - % - % p.show() - % make the outline of the panel "p" show up in red - % in the figure window. this is useful for - % understanding a complex layout. - % - % see also: identify() - - r = p.getObjectPosition(); - - if ~isempty(r) - h = p.getShowAxis(); - delete(get(h, 'children')); - xdata = [r(1) r(1)+r(3) r(1)+r(3) r(1) r(1)]; - ydata = [r(2) r(2) r(2)+r(4) r(2)+r(4) r(2)]; - plot(h, xdata, ydata, 'r-', 'linewidth', 5); - axis([0 1 0 1]) - end - - end - - function export(p, varargin) - - % to export the root panel to an image file - % - % p.export(filename, ...) - % - % export the figure containing panel "p" to an image file. - % you must supply the filename of this output file, with or - % without a file extension. any further arguments must be - % option strings starting with the dash ("-") character. "p" - % should be the root panel. - % - % if the filename does not include an extension, the - % appropriate extension will be added. if it does, the - % output format will be inferred from it, unless overridden - % by the "-o" option, described below. - % - % if you are targeting a print publication, you may find it - % easiest to size your output using the "paper sizing model" - % (below). if you prefer, you can use the "direct sizing - % model", instead. these two sizing models are described - % below. underneath these are listed the options unrelated - % to sizing (which apply regardless of which sizing model - % you use). - % - % - % - % PAPER SIZING MODEL: - % - % using the paper sizing model, you specify your target as a - % region of a piece of paper, and the actual size in - % millimeters is calculated for you. this is usually very - % convenient, but if you find it unsuitable, the direct - % sizing model (next section) is provided as an alternative. - % - % to specify the region, you specify the type (size) of - % paper, the orientation, the number of columns, and the - % aspect ratio of the figure (or the fraction of a column to - % fill). usually, the remaining options can be left as - % defaults. - % - % -pX - % X is the paper type, A2-A6 or letter (default is A4). - % NB: you can also specify paper type LNCS (Lecture Notes - % in Computer Science), using "-pLNCS". If you do this, - % the margins are also adjusted to match LNCS format. - % - % -l - % specify landscape mode (default is portrait). - % - % -mX - % X is the paper margins in mm. you can provide a scalar - % (same margins all round) or a comma-separated list of - % four values, specifying the left, bottom, right, top - % margins separately (default is 20mm all round). - % - % -iX - % X is the inter-column space in mm (default is - % 5mm). - % - % -cX - % X is the number of columns (default is 1). - % - % NB: the following two options represent two ways to - % specify the height of the figure relative to the space - % defined by the above options. if you supply both, - % whichever comes second will be used. - % - % -aX - % X is the aspect ratio of the resulting image file (width - % is set by the paper model). X can be one of the strings: - % s (square), g (landscape golden ratio), gp (portrait - % golden ratio), h (half-height), d (double-height); or, a - % number greater than zero, to specify the aspect ratio - % explicitly. note that, if using the numeric form, the - % ratio is expressed as the quotient of width over height, - % in the usual way. ratios greater than 10 or less than - % 0.1 are disallowed, since these can cause a very large - % figure file to be created accidentally. default is to - % use the landscape golden ratio. - % - % -fX - % X is the fraction of the column (or page, if there are - % not columns) to fill. X can be one of the following - % strings - a (all), tt (two thirds), h (half), t (third), - % q (quarter) - or a fraction between 0 and 1, to specify - % the fraction of the space to fill as a number. default - % is to use aspect ratio, not fill fraction. - % - % - % - % DIRECT SIZING MODEL: - % - % if one of these two options is set, the output image is - % sized according to that option and the aspect ratio (see - % above) and the paper model is not used. if both are set, - % the aspect ratio is not used either. - % - % -wX - % X is the explicit width in mm. - % - % -hX - % X is the explicit height in mm. - % - % - % - % NON-SIZING OPTIONS: - % - % finally, a few options are provided to control how - % the prepared figure is exported. note that DPI below - % 150 is only recommended for sizing drafts, since - % font and line sizes are not rendered even vaguely - % accurately in some cases. at the other end, DPI - % above 600 is unlikely to be useful except when - % submitting camera-ready copy. - % - % -rX - % X is the resolution (DPI) at which to produce the - % output file. X can be one of the following strings - % - d (draft, 75DPI), n (normal, 150DPI), h (high, - % 300DPI), p (publication quality, 600DPI), x - % (extremely high quality, 1200DPI) - or just - % the DPI as a number (must be in 75-2400). the - % default depends on the output format (see below). - % - % -rX/S - % X is the DPI, S is the smoothing factor, which can - % be 2 or 4. the output file is produced at S times - % the specified DPI, and then reduced in size to the - % specified DPI by averaging. thus, the hard edges - % produced by the renderer are smoothed - the effect - % is somewhat like "anti-aliasing". - % - % NB: the DPI setting might be expected to have no - % effect on vector formats. this is true for SVG, but - % not for EPS, where the DPI affects the numerical - % precision used as well as the size of some image - % elements, but has little effect on file size. for - % this reason, the default DPI is 150 for bitmap - % formats but 600 for vector formats. - % - % -s - % print sideways (default is to print upright) - % - % -oX - % X is the output format - choose from most of the - % built-in image device drivers supported by "print" - % (try "help print"). this includes "png", "jpg", - % "tif", "eps" and "pdf". note that "eps"/"ps" - % resolve to "epsc2"/"psc2", for convenience. to use - % the "eps"/"ps" devices, use "-oeps!"/"-ops!". you - % may also specify "svg", if you have the tool - % "plot2svg" on your path (available at Matlab - % Central File Exchange). the default output format - % is inferred from the file extension, or "png" if - % the passed filename has no extension. - % - % - % - % EXAMPLES: - % - % default export of 'myfig', creates 'myfig.png' at a - % size of 170x105mm (1004x620px). this size comes - % from: A4 (210mm wide), minus two 20mm margins - % (170mm), and using the golden aspect ratio to give a - % height of 105mm, and finally 150DPI to give the - % pixel size. - % - % p.export('myfig') - % - % when producing the final camera-ready image for a - % square figure that will sit in one of the two - % columns of a letter-size paper journal with default - % margins and inter-column space, we might use this: - % - % p.export('myfig', '-pletter', '-c2', '-as', '-rp'); - - % LEGACY - % - % (this is legacy since the 'defer' flag is no longer - % needed - though it is still supported) - % - % NB: if you pass 'defer' to the constructor, calling - % export() both exports the panel and releases the - % defer mode. future changes to properties (e.g. - % margins) will cause immediate recomputation of the - % layout. - - % check - if ~p.isRoot() - error('panel:ExportWhenNotRoot', 'cannot export() this panel - it is not the root panel'); - end - - % used below - default_margin = 20; - - % parameters - pars = []; - pars.filename = ''; - pars.fmt = ''; - pars.ext = ''; - pars.dpi = []; - pars.smooth = 1; - pars.paper = 'A4'; - pars.landscape = false; - pars.fill = -1.618; - pars.cols = 1; - pars.intercolumnspacing = 5; - pars.margin = default_margin; - pars.sideways = false; - pars.width = 0; - pars.height = 0; - invalid = false; - - % interpret args - for a = 1:length(varargin) - - % extract - arg = varargin{a}; - - % all arguments must be non-empty strings - if ~isstring(arg) - error('panel:InvalidArgument', ... - 'all arguments to export() must be non-empty strings'); - end - - % is filename? - if arg(1) ~= '-' - - % error if already set - if ~isempty(pars.filename) - error('panel:InvalidArgument', ... - ['at argument "' arg '", the filename is already set ("' pars.filename '")']); - end - - % ok, continue - pars.filename = arg; - continue - - end - - % split off option key and option value - if length(arg) < 2 - error('panel:InvalidArgument', ... - ['at argument "' arg '", no option specified']); - end - key = arg(2); - val = arg(3:end); - - % switch on option key - switch key - - case 'p' - pars.paper = validate_par(val, arg, {'A2' 'A3' 'A4' 'A5' 'A6' 'letter' 'LNCS'}); - - case 'l' - pars.landscape = true; - validate_par(val, arg, 'empty'); - - case 'm' - pars.margin = validate_par(str2num(val), arg, 'dimension', 'nonneg'); - - case 'i' - pars.intercolumnspacing = validate_par(str2num(val), arg, 'scalar', 'nonneg'); - - case 'c' - pars.cols = validate_par(str2num(val), arg, 'scalar', 'integer'); - - case 'f' - switch val - case 'a', pars.fill = 1; % all - case 'w', pars.fill = 1; % whole (legacy, not documented) - case 'tt', pars.fill = 2/3; % two thirds - case 'h', pars.fill = 1/2; % half - case 't', pars.fill = 1/3; % third - case 'q', pars.fill = 1/4; % quarter - otherwise - pars.fill = validate_par(str2num(val), arg, 'scalar', [0 1]); - end - - case 'a' - switch val - case 's', pars.fill = -1; % square - case 'g', pars.fill = -1.618; % golden ratio (landscape) - case 'gp', pars.fill = -1/1.618; % golden ratio (portrait) - case 'h', pars.fill = -2; % half height - case 'd', pars.fill = -0.5; % double height - otherwise - pars.fill = -validate_par(str2num(val), arg, 'scalar', [0.1 10]); - end - - case 'w' - pars.width = validate_par(str2num(val), arg, 'scalar', 'nonneg', [10 Inf]); - - case 'h' - pars.height = validate_par(str2num(val), arg, 'scalar', 'nonneg', [10 Inf]); - - case 'r' - % peel off smoothing ("/...") - if any(val == '/') - f = find(val == '/', 1); - switch val(f+1:end) - case '2', pars.smooth = 2; - case '4', pars.smooth = 4; - otherwise, error('panel:InvalidArgument', ... - ['invalid argument "' arg '", part after / must be "2" or "4"']); - end - val = val(1:end-2); - end - - switch val - case 'd', pars.dpi = 75; % draft - case 'n', pars.dpi = 150; % normal - case 'h', pars.dpi = 300; % high - case 'p', pars.dpi = 600; % publication quality - case 'x', pars.dpi = 1200; % extremely high quality - otherwise - pars.dpi = validate_par(str2num(val), arg, 'scalar', [75 2400]); - end - - case 's' - pars.sideways = true; - validate_par(val, arg, 'empty'); - - case 'o' - fmts = { - 'png' 'png' 'png' - 'tif' 'tiff' 'tif' - 'tiff' 'tiff' 'tif' - 'jpg' 'jpeg' 'jpg' - 'jpeg' 'jpeg' 'jpg' - 'ps' 'psc2' 'ps' - 'ps!' 'psc' 'ps' - 'psc' 'psc' 'ps' - 'ps2' 'ps2' 'ps' - 'psc2' 'psc2' 'ps' - 'eps' 'epsc2' 'eps' - 'eps!' 'eps' 'eps' - 'epsc' 'epsc' 'eps' - 'eps2' 'eps2' 'eps' - 'epsc2' 'epsc2' 'eps' - 'pdf' 'pdf' 'pdf' - 'svg' 'svg' 'svg' - }; - validate_par(val, arg, fmts(:, 1)'); - index = isin(fmts(:, 1), val); - pars.fmt = fmts(index, 2:3); - - otherwise - error('panel:InvalidArgument', ... - ['invalid argument "' arg '", option is not recognised']); - - end - - end - - % if not specified, infer format from filename - if isempty(pars.fmt) - [path, base, ext] = fileparts(pars.filename); - if ~isempty(ext) - ext = ext(2:end); - end - switch ext - case {'tif' 'tiff'} - pars.fmt = {'tiff' 'tif'}; - case {'jpg' 'jpeg'} - pars.fmt = {'jpeg' 'jpg'}; - case 'eps' - pars.fmt = {'epsc2' 'eps'}; - case {'png' 'pdf' 'svg'} - pars.fmt = {ext ext}; - case '' - pars.fmt = {'png' 'png'}; - otherwise - warning('panel:CannotInferImageFormat', ... - ['unable to infer image format from file extension "' ext '" (PNG assumed)']); - pars.fmt = {'png' 'png'}; - end - end - - % extract - pars.ext = pars.fmt{2}; - pars.fmt = pars.fmt{1}; - - % extract - is_bitmap = ismember(pars.fmt, {'png' 'jpeg' 'tiff'}); - - % default DPI - if isempty(pars.dpi) - if is_bitmap - pars.dpi = 150; - else - pars.dpi = 600; - end - end - - % validate - if isequal(pars.fmt, 'svg') && isempty(which('plot2svg')) - error('panel:Plot2SVGMissing', 'export to SVG requires plot2svg (Matlab Central File Exchange)'); - end - - % validate - if ~is_bitmap && pars.smooth ~= 1 - pars.smooth = 1; - warning('panel:NoSmoothVectorFormat', 'requested smoothing will not be performed (chosen export format is not a bitmap format)'); - end - - % validate - if isempty(pars.filename) - error('panel:InvalidArgument', 'filename not supplied'); - end - - % make sure filename has extension - if ~any(pars.filename == '.') - pars.filename = [pars.filename '.' pars.ext]; - end - - - -%%%% GET TARGET DIMENSIONS (BEGIN) - - % get space for figure - switch pars.paper - case 'A0', sz = [841 1189]; - case 'A1', sz = [594 841]; - case 'A2', sz = [420 594]; - case 'A3', sz = [297 420]; - case 'A4', sz = [210 297]; - case 'A5', sz = [148 210]; - case 'A6', sz = [105 148]; - case 'letter', sz = [216 279]; - case 'LNCS', sz = [152 235]; - % if margin is still at default, set it to LNCS - % margin size - if isequal(pars.margin, default_margin) - pars.margin = [15 22 15 20]; - end - otherwise - error(['unrecognised paper size "' pars.paper '"']) - end - - % orientation of paper - if pars.landscape - sz = sz([2 1]); - end - - % paper margins (scalar or quad) - if isscalar(pars.margin) - pars.margin = pars.margin * [1 1 1 1]; - end - sz = sz - pars.margin(1:2) - pars.margin(3:4); - - % divide by columns - w = (sz(1) + pars.intercolumnspacing) / pars.cols - pars.intercolumnspacing; - sz(1) = w; - - % apply fill / aspect ratio - if pars.fill > 0 - % fill fraction - sz(2) = sz(2) * pars.fill; - elseif pars.fill < 0 - % aspect ratio - sz(2) = sz(1) * (-1 / pars.fill); - end - - % direct sizing model is used if either of width or height - % is set - if pars.width || pars.height - - % use aspect ratio to fill in either one that is not - % specified - if ~pars.width || ~pars.height - - % aspect ratio must not be a fill - if pars.fill >= 0 - error('cannot use fill fraction with direct sizing model'); - end - - % compute width - if ~pars.width - pars.width = pars.height * -pars.fill; - end - - % compute height - if ~pars.height - pars.height = pars.width / -pars.fill; - end - - end - - % store - sz = [pars.width pars.height]; - - end - -%%%% GET TARGET DIMENSIONS (END) - - - - % orientation of figure is upright, unless printing - % sideways, in which case the printing space is rotated too - if pars.sideways - set(p.h_figure, 'PaperOrientation', 'landscape') - sz = fliplr(sz); - else - set(p.h_figure, 'PaperOrientation', 'portrait') - end - - % report export size - msg = ['exporting to ' int2str(sz(1)) 'x' int2str(sz(2)) 'mm']; - if is_bitmap - psz = sz / 25.4 * pars.dpi; - msg = [msg ' (' int2str(psz(1)) 'x' int2str(psz(2)) 'px @ ' int2str(pars.dpi) 'DPI)']; - else - msg = [msg ' (vector format @ ' int2str(pars.dpi) 'DPI)']; - end - disp(msg); - - % if we are in defer state, we need to do a clean - % recompute first so that axes get positioned so that - % axis ticks get set correctly (if they are in - % automatic mode), since the LAYOUT_MODE_PREPRINT - % recompute will store the tick states. - if p.state.defer - p.state.defer = 0; - p.recomputeLayout([]); - end - - % turn off defer, if it is on - p.state.defer = 0; - - % do a pre-print layout - context.mode = panel.LAYOUT_MODE_PREPRINT; - context.size_in_mm = sz; - context.rect = [0 0 1 1]; - p.recomputeLayout(context); - - % need also to disable the warning that we should set - % PaperPositionMode to auto during this operation - - % we're setting it explicitly. - w = warning('off', 'MATLAB:Print:CustomResizeFcnInPrint'); - - % handle smoothing - pars.write_dpi = pars.dpi; - if pars.smooth > 1 - pars.write_dpi = pars.write_dpi * pars.smooth; - print_filename = [pars.filename '-temp']; - else - print_filename = pars.filename; - end - - % disable layout so it doesn't get computed during any - % figure resize operations that occur during printing. - p.state.defer = 1; - - % set size of figure now. it's important we do this - % after the pre-print layout, because in SVG export - % mode the on-screen figure size is changed and that - % would otherwise affect ticks and limits. - switch pars.fmt - - case 'svg' - % plot2svg (our current SVG export mechanism) uses - % 'Units' and 'Position' (i.e. on-screen position) - % rather than the Paper- prefixed ones used by the - % Matlab export functions. - - % store old on-screen position - svg_units = get(p.h_figure, 'Units'); - svg_pos = get(p.h_figure, 'Position'); - - % update on-screen position - set(p.h_figure, 'Units', 'centimeters'); - pos = get(p.h_figure, 'Position'); - pos(3:4) = sz / 10; - set(p.h_figure, 'Position', pos); - - otherwise - set(p.h_figure, ... - 'PaperUnits', 'centimeters', ... - 'PaperPosition', [0 0 sz] / 10, ... - 'PaperSize', sz / 10 ... % * 1.5 / 10 ... % CHANGED 21/06/2011 so that -opdf works correctly - why was this * 1.5, anyway? presumably was spurious... - ); - - end - - % do fixdash (not for SVG, since plot2svg does a nice - % job of dashed lines without our meddling...) - if ~isequal(pars.fmt, 'svg') - p.do_fixdash(context); - end - - % do the export - switch pars.fmt - case 'svg' - plot2svg(print_filename, p.h_figure); - otherwise - print(p.h_figure, '-loose', ['-d' pars.fmt], ['-r' int2str(pars.write_dpi)], print_filename) - end - - % undo fixdash - if ~isequal(pars.fmt, 'svg') - p.do_fixdash([]); - end - - % set on-screen figure size back to what it was, if it - % was changed. - switch pars.fmt - case 'svg' - set(p.h_figure, 'Units', svg_units); - set(p.h_figure, 'Position', svg_pos); - end - - % enable layout again (it was disabled, above, during - % printing). - p.state.defer = 0; - - % enable warnings - warning(w); - - % do a post-print layout - context.mode = panel.LAYOUT_MODE_POSTPRINT; - context.size_in_mm = []; - context.rect = [0 0 1 1]; - p.recomputeLayout(context); - - % handle smoothing - if pars.smooth > 1 - psz = sz * pars.smooth / 25.4 * pars.dpi; - msg = [' (reducing from ' int2str(psz(1)) 'x' int2str(psz(2)) 'px)']; - disp(['smoothing by factor ' int2str(pars.smooth) msg]); - im1 = imread(print_filename); - delete(print_filename); - sz = size(im1); - sz = [sz(1)-mod(sz(1),pars.smooth) sz(2)-mod(sz(2),pars.smooth)] / pars.smooth; - im = zeros(sz(1), sz(2), 3); - mm = 1:pars.smooth:(sz(1) * pars.smooth); - nn = 1:pars.smooth:(sz(2) * pars.smooth); - for m = 0:pars.smooth-1 - for n = 0:pars.smooth-1 - im = im + double(im1(mm+m, nn+n, :)); - end - end - im = uint8(im / (pars.smooth^2)); - - % set the DPI correctly in the new file - switch pars.fmt - case 'png' - dpm = pars.dpi / 25.4 * 1000; - imwrite(im, pars.filename, ... - 'XResolution', dpm, ... - 'YResolution', dpm, ... - 'ResolutionUnit', 'meter'); - case 'tiff' - imwrite(im, pars.filename, ... - 'Resolution', pars.dpi * [1 1]); - otherwise - imwrite(im, pars.filename); - end - end - - end - - function clearCallbacks(p) - - % clear all callback functions for the panel - % - % p.clearCallbacks() - p.m_callback = {}; - - end - - function setCallback(p, func, userdata) - - % set the callback function for the panel - % - % p.setCallback(myCallbackFunction, userdata) - % - % NB: this function clears all current callbacks, then - % calls addCallback(myCallbackFunction, userdata). - p.clearCallbacks(); - p.addCallback(func, userdata); - - end - - function addCallback(p, func, userdata) - - % attach a callback function to receive panel events - % - % p.addCallback(myCallbackFunction, userdata) - % register myCallbackFunction() to be called when - % events occur on the panel. at present, the only - % event is "layout-updated", which usually occurs - % after the figure is resized. myCallbackFunction() - % should accept one argument, "data", which will - % have the following fields. - % - % "userdata": the userdata passed to this function, if - % any was supplied, else empty. - % - % "panel": a reference to the panel on which the - % callback was set. this object can be queried in - % the usual way. - % - % "event": name of event (currently only - % "layout-updated"). - % - % "context": the layout context for the panel. this - % includes a field "size_in_mm" which is the - % physical size of the rendering surface (screen - % real estate, or image file) and "rect" which is - % the relative size of the rectangle within that - % occupied by the panel which is the context of - % the callback (in [left, bottom, width, height] - % format). - - invalid = ~isscalar(func) || ~isa(func, 'function_handle'); - if invalid - error('panel:InvalidArgument', 'argument to callback() must be a function handle'); - end - if nargin == 2 - p.m_callback{end+1} = {func []}; - else - p.m_callback{end+1} = {func userdata}; - end - - end - - function q = find(p, varargin) - - % find panel according to some search conditions - % - % p.find(...) - % you can use this to recover the panel - % associated with a particular graphics - % object, for example. conditions are - % specified as {type, data} pairs, as listed - % below. - % - % {'object', h} - % returned panels must be managing the object - % "h". - % - % example: - % q = p.find({'object', h_axis}) - - % get all panels - f = p.getPanels('*'); - - % return value - q = {}; - - % search - for i = 1:length(f) - - % get panel - p = f{i}; - - % check conditions - for c = 1:length(varargin) - - % get condition - cond = varargin{c}; - - % switch on type - switch cond{1} - - case 'object' - h = cond{2}; - if ~any(h == p.h_object) - p = []; - end - - otherwise - error(['unrecognised condition type "' cond{1} '"']); - - end - - end - - % if still there - if ~isempty(p) - q{end+1} = p; - end - - end - - end - - function identify(p) - - % add annotations to help identify individual panels - % - % p.identify() - % when creating a complex layout, it can become - % confusing as to which panel is which. this - % function adds a text label to each axis panel - % indicating how to reference the axis panel through - % the root panel. for instance, if "(2, 3)" is - % indicated, you can find that panel at p(2, 3). - % - % see also: show() - - if p.isObject() - - % get managed axes - h_axes = p.getAllManagedAxes(); - - % if no axes, ignore - if isempty(h_axes) - return - end - - % mark first axis - h_axes = h_axes(1); - cla(h_axes); - text(0.5, 0.5, p.state.name, 'fontsize', 12, 'hori', 'center', 'parent', h_axes); - axis(h_axes, [0 1 0 1]); - grid(h_axes, 'off') - - else - - % recurse - for c = 1:length(p.m_children) - p.m_children(c).identify(); - end - - end - - end - - function repack(p, packspec) - - % change the packing specifier for an existing panel - % - % p.repack(packspec) - % repack() is a convenience function provided to - % allow easy development of a layout from the - % command prompt. packspec can be any packing - % specifier accepted by pack(). - % - % see also: pack() - - % deny repack() on root - if p.isRoot() - - % let's deny this. I'm not sure it makes anyway. you - % could always pack into root with a panel with - % absolute positioning, so let's deny first, and - % allow later if we're sure it's a good idea. - error('panel:InvalidArgument', 'root panel cannot be repack()ed'); - - end - - % validate - validate_packspec(packspec); - - % handle units - if iscell(packspec) - units = p.getPropertyValue('units'); - packspec{1} = panel.resolveUnits({packspec{1} units}, 'mm'); - end - - % update the packspec - p.packspec = packspec; - - % and recomputeLayout - p.recomputeLayout([]); - - end - - function pack(p, varargin) - - % add (pack) child panel(s) into an existing panel - % - % p.pack(...) - % add children to the panel "p", committing it as a - % "parent" panel (if it is not already). new (child) - % panels are created using this call - they start as - % "uncommitted" panels. if the parent already has - % children, the new children are appended. The - % following arguments are understood: - % - % 'h'/'v' - switch "p" to pack in the horizontal or - % vertical packing dimension for relative packing - % mode (default for new panels is vertical). - % - % {a, b, c, ...} (a cell row vector) - pack panels - % into "p" with "packing specifiers" a, b, c, etc. - % packing specifiers are detailed below. - % - % PACKING MODES - % panels can be packed into their parent in two - % modes, dependent on their packing specifier. you - % can see a visual representation of these modes on - % the HTML documentation page "Layout". - % - % (i) Relative Mode - panels are packed into the space - % occupied by their parent. size along the parent's - % "packing dimension" is dictated by the packing - % specifier; along the other dimension size matches - % the parent. the following packing specifiers - % indicate Relative Mode. - % - % a) Fixed Size: the specifier is a scalar double in - % a cell {d}. The panel will be of size d in the - % current units of "p" (see the property "p.units" - % for details, but default units are mm). - % - % b) Fractional Size: the specifier is a scalar - % double between 0 and 1 (or between 1 and 100, as a - % percentage). The panel is sized as a fraction of - % the space remaining in its parent after Fixed Size - % panels and inter-panel margins have been subtracted. - % - % c) Stretchable: the specifier is the empty matrix - % []. remaining space in the parent after Fixed and - % Fractional Size panels have been subtracted is - % shared out amongst Stretchable Size panels. - % - % (ii) Absolute Mode - panels hover above their - % parent and do not take up space, as if using the - % position:absolute property in CSS. The packing - % specifier is a 1x4 double vector indicating the - % [left bottom width height] of the panel in - % normalised coordinates of its parent. for example, - % the specifier [0 0 1 1] generates a child panel - % that fills its parent. - % - % SHORTCUTS - % - % ** a small scalar integer, N, (1 to 32) is expanded - % to {[], [], ... []}, with N entries. that is, it - % packs N panels in Relative Mode (Stretchable) and - % shares the available space between them. - % - % ** the call to pack() is recursive, so following a - % packing specifier list, an additional list will - % be used to generate a separate call to pack() on - % each of the children created by the first. hence: - % - % p.pack({[] []}, {[] []}) - % - % will create a 2x2 grid of panels that share the - % space of their parent, "p". since the argument - % "2" expands to {[] []} (see above), the same grid - % can be created using: - % - % p.pack(2, 2) - % - % which is a common idiom in the demos. NB: on - % recursion, the packing dimension is flipped - % automatically, so that a grid is formed. - % - % ** if no arguments are passed at all, a single - % argument {[]} is assumed, so that a single - % additional panel is packed into the parent in - % relative packing mode and with stretchable size. - % - % see also: panel (overview), panel/panel(), select() - % - % LEGACY - % - % the interface to pack() was changed at release - % 2.10 to add support for panels of fixed physical - % size. the interface offered at 2.9 and earlier is - % still available (look inside panel.m - search for - % text "LEGACY" - for details). - - % LEGACY - % - % releases of panel prior to 2.10 did not support - % panels of fixed physical size, and therefore had - % developed a different argument form to that used in - % 2.10 and beyond. specifically, the following - % additional arguments are accepted, for legacy - % support: - % - % 'abs' - % the next argument will be an absolute position, as - % described below. you should avoid using absolute - % positioning mode, in general, since this does not - % take advantage of panel's automatic layout. - % however, on occasion, you may need to take manual - % control of the position of one or more panels. see - % demopanelH for an example. - % - % 1xN row vector (without 'abs') - % pack N new panels along the packing dimension in - % relative mode, with the relative size of each - % given by the elements of the vector. -1 can be - % passed for any elements to mark those panel as - % 'stretchable', so that they fill available space - % left over by other panels packed alongside. the - % sum of the vector (apart from any -1 entries) - % should not come to more than 1, or a warning will - % be generated during laying out. an example would - % be [1/4 1/4 -1], to pack 3 panels, at 25, 25 and - % 50% relative sizes. though, NB, you can use - % percentages instead of fractions if you prefer, in - % which case they should not sum to over 100. so - % that same pack() would be [25 25 -1]. - % - % 1x4 row vector (after 'abs') - % pack 1 new panel using absolute positioning. the - % argument indicates the [left bottom width height] - % of the new panel, in normalised coordinates, as a - % fraction of its parent's position. panels using - % absolute positioning mode are ignored for the sake - % of layout, much like items using - % 'position:absolute' in CSS. - - % handle legacy, parse arguments from varargin into args - args = {}; - while ~isempty(varargin) - - % peel - arg = varargin{1}; - varargin = varargin(2:end); - - % handle shortcut (small integer) on current interface - if isa(arg, 'double') && isscalar(arg) && round(arg) == arg && arg >= 1 && arg <= 32 - arg = cell(1, arg); - end - - % handle current interface - note that the argument - % "recursive" is private and not advertised to the - % user. - if isequal(arg, 'h') || isequal(arg, 'v') || (iscell(arg) && isrow(arg)) || isequal(arg, 'recursive') - args{end+1} = arg; - continue - end - - % report (DEBUG) -% panel.debugmsg('use of LEGACY interface to pack()', 1); - - % handle legacy case - if isequal(arg, 'abs') - if length(varargin) ~= 1 || ~isnumeric(varargin{1}) || ~isofsize(varargin{1}, [1 4]) - error('panel:LegacyAbsNotFollowedBy1x4', 'the argument "abs" on the legacy interface should be followed by a [1x4] row vector'); - end - abs = varargin{1}; - varargin = varargin(2:end); - args{end+1} = {abs}; - continue - end - - % handle legacy case - if isa(arg, 'double') && isrow(arg) - arg_ = {}; - for a = 1:length(arg) - aa = arg(a); - if isequal(aa, -1) - arg_{end+1} = []; - else - arg_{end+1} = aa; - end - end - args{end+1} = arg_; - continue - end - - % unrecognised argument - error('panel:InvalidArgument', 'argument to pack() not recognised'); - - end - - % check m_panelType - if p.isObject() - error('panel:PackWhenObjectPanel', ... - 'cannot pack() into this panel - it is already committed as an object panel'); - end - - % if no arguments, simulate an argument of [], to pack - % a single panel of stretchable size - if isempty(args) - args = {{[]}}; - end - - % state - recursive = false; - - % handle arguments one by one - while ~isempty(args) && ischar(args{1}) - - % extract - arg = args{1}; - args = args(2:end); - - % handle string arguments - switch arg - case 'h' - p.packdim = 1; - case 'v' - p.packdim = 2; - case 'recursive' - recursive = true; - otherwise - error('panel:InvalidArgument', ['pack() did not recognise the argument "' arg '"']); - end - - end - - % if no more arguments that's weird but not bad - if isempty(args) - return - end - - % next argument now must be a cell - arg = args{1}; - args = args(2:end); - if ~iscell(arg) - panel.error('InternalError'); - end - - % commit as parent - p.commitAsParent(); - - % for each element - for i = 1:length(arg) - - % get packspec - packspec = arg{i}; - - % validate - validate_packspec(packspec); - - % handle units - if iscell(packspec) - units = p.getPropertyValue('units'); - packspec{1} = panel.resolveUnits({packspec{1} units}, 'mm'); - end - - % create a child - child = panel('pack', p); - child.packspec = packspec; - - % store it in the parent - if isempty(p.m_children) - p.m_children = child; - else - p.m_children(end+1) = child; - end - - % recurse (further argumens are passed on) - if ~isempty(args) - child_packdim = flippackdim(p.packdim); - edges = 'hv'; - child.pack('recursive', edges(child_packdim), args{:}); - end - - end - - % this must generate a recomputeLayout(), since the - % addition of new panels may affect the layout. any - % recursive call passes 'recursive', so that only the - % root call actually bothers doing a layout. - if ~recursive - p.recomputeLayout([]); - end - - end - - function h_out = select(p, h_object) - - % create or select an axis or object panel - % - % h = p.select(h) - % this call will return the handle of the object - % associated with the panel. if the panel is not yet - % committed, this will involve first committing it - % as an "object panel". if a list of objects ("h") - % is passed, these are the objects associated with - % the panel; if not, a new axis is created by the - % panel when this function is called. - % - % if the object list includes axes, then the "object - % panel" is also known as an "axis panel". in this - % case, the call to select() will make the (first) - % axis current, unless an output argument is - % requested, in which case the handle of the axes - % are returned but no axis is made current. - % - % the passed objects can be user-created axes (e.g. - % a colorbar) or any graphics object that is to have - % its position managed (e.g. a uipanel). your - % mileage may vary with different types of graphics - % object, please let me know. - % - % see also: panel (overview), panel/panel(), pack() - - % handle "all" and "data" - if nargin == 2 && isstring(h_object) && (strcmp(h_object, 'all') || strcmp(h_object, 'data')) - - % collect - h_out = []; - - % commit all uncommitted panels as axis panels by - % selecting them once - if p.isParent() - - % recurse - for c = 1:length(p.m_children) - h_out = [h_out p.m_children(c).select(h_object)]; - end - - elseif p.isUncommitted() - - % select in an axis - h_out = p.select(); - - % plot some data - if strcmp(h_object, 'data') - plot(h_out, randn(100, 1), 'k-'); - end - - end - - % ok - return - - end - - % check m_panelType - if p.isParent() - error('panel:SelectWhenParent', 'cannot select() this panel - it is already committed as a parent panel'); - end - - % commit as object - p.commitAsObject(); - - % assume not a new object - newObject = false; - - % use passed graphics object - if nargin >= 2 - - % validate - if ~all(ishandle(h_object)) - error('panel:InvalidArgument', 'argument to select() must be a list of handles to graphics objects'); - end - - % validate - if ~isempty(p.h_object) - % 02/08/19 I disabled this check because - % I don't see why it's needed (why - % should we not change the managed - % objects on the fly?) -% error('panel:SelectWithObjectWhenObject', 'cannot select() new objects into this panel - it is already managing objects'); - end - - % store - p.h_object = h_object; - newObject = true; - - % make sure it has the correct parent - this doesn't - % seem to affect axes, so we do it for all - set(p.h_object, 'parent', p.h_parent); - - end - - % create new axis if necessary - if isempty(p.h_object) - % 'NextPlot', 'replacechildren' - % make sure fonts etc. don't get changed when user - % plots into it - p.h_object = axes( ... - 'Parent', p.h_parent, ... - 'NextPlot', 'replacechildren' ... - ); - newObject = true; - end - - % if wrapped objects include an axis, and no output args, make it current - h_axes = p.getAllManagedAxes(); - if ~isempty(h_axes) && ~nargout - set(p.h_figure, 'CurrentAxes', h_axes(1)); - - % 12/07/11: this call is slow, because it implies "drawnow" -% figure(p.h_figure); - - % 12/07/11: this call is fast, because it doesn't - set(0, 'CurrentFigure', p.h_figure); - - end - - % and return object list - if nargout - h_out = p.h_object; - end - - % this must generate a applyLayout(), since the axis - % will need positioning appropriately - if newObject - % 09/03/12 mitch - % if there isn't a context yet, we'll have to - % recomputeLayout(), in fact, to generate a context first. - % this will happen, for instance, if a single panel - % is generated in a window that was already open - % (no resize event will fire, and since pack() is - % not called, it will not call recomputeLayout() either). - % nonetheless, we have to reposition this object, so - % this forces us to recomputeLayout() now and generate - % that context we need. - if isempty(p.m_context) - p.recomputeLayout([]); - else - p.applyLayout(); - end - end - - end - - end - - - - - - - - - - - - - - %% ---- HIDDEN OVERLOADS ---- - - methods (Hidden = true) - - function out = vertcat(p, q) - error('panel2:MethodNotImplemented', 'concatenation is not supported by panel (use a cell array instead)'); - end - - function out = horzcat(p, q) - error('panel2:MethodNotImplemented', 'concatenation is not supported by panel (use a cell array instead)'); - end - - function out = cat(dim, p, q) - error('panel2:MethodNotImplemented', 'concatenation is not supported by panel (use a cell array instead)'); - end - - function out = ge(p, q) - error('panel2:MethodNotImplemented', 'inequality operators are not supported by panel'); - end - - function out = le(p, q) - error('panel2:MethodNotImplemented', 'inequality operators are not supported by panel'); - end - - function out = lt(p, q) - error('panel2:MethodNotImplemented', 'inequality operators are not supported by panel'); - end - - function out = gt(p, q) - error('panel2:MethodNotImplemented', 'inequality operators are not supported by panel'); - end - - function out = eq(p, q) - out = eq@handle(p, q); - end - - function out = ne(p, q) - out = ne@handle(p, q); - end - - end - - - - - - - - - - - - - - - - - - - - - - - - - - %% ---- PUBLIC HIDDEN GET/SET ---- - - methods (Hidden = true) - - function p = descend(p, indices) - - while ~isempty(indices) - - % validate - if numel(p) > 1 - error('panel:InvalidIndexing', 'you can only use () on a single (scalar) panel'); - end - - % validate - if ~p.isParent() - error('panel:InvalidIndexing', 'you can only use () on a parent panel'); - end - - % extract - index = indices{1}; - indices = indices(2:end); - - % only accept numeric - if ~isnumeric(index) || ~isscalar(index) - error('panel:InvalidIndexing', 'you can only use () with scalar indices'); - end - - % do the reference - p = p.m_children(index); - - end - - end - - function p_out = subsasgn(p, refs, value) - - % output is always subject - p_out = p; - - % handle () indexing - if strcmp(refs(1).type, '()') - p = p.descend(refs(1).subs); - refs = refs(2:end); - end - - % is that it? - if isempty(refs) - error('panel:InvalidIndexing', 'you cannot assign to a child panel'); - end - - % next ref must be . - if ~strcmp(refs(1).type, '.') - panel.error('InvalidIndexing'); - end - - % either one (.X) or two (.ch.X) - switch numel(refs) - - case 2 - - % validate - if ~strcmp(refs(2).type, '.') - panel.error('InvalidIndexing'); - end - - % validate - switch refs(2).subs - case {'fontname' 'fontsize' 'fontweight'} - case {'margin' 'marginleft' 'marginbottom' 'marginright' 'margintop'} - otherwise - panel.error('InvalidIndexing'); - end - - % avoid computing layout whilst setting descendant - % properties - p.defer(); - - % recurse - switch refs(1).subs - case {'children' 'ch'} - cs = p.m_children; - for c = 1:length(cs) - subsasgn(cs(c), refs(2:end), value); - end - case {'descendants' 'de'} - cs = p.getPanels('*'); - for c = 1:length(cs) - if cs{c} ~= p - subsasgn(cs{c}, refs(2:end), value); - end - end - case {'family' 'fa'} - cs = p.getPanels('*'); - for c = 1:length(cs) - subsasgn(cs{c}, refs(2:end), value); - end - end - - % release for laying out - p.undefer(); - - % mark for appropriate updates - refs(1).subs = refs(2).subs; - - case 1 - - % delegate - p.setPropertyValue(refs(1).subs, value); - - otherwise - panel.error('InvalidIndexing'); - - end - - % update layout as necessary - switch refs(1).subs - case {'fontname' 'fontsize' 'fontweight'} - p.applyLayout('recurse'); - case {'margin' 'marginleft' 'marginbottom' 'marginright' 'margintop'} - p.recomputeLayout([]); - end - - end - - function out = subsref(p, refs) - - % handle () indexing - if strcmp(refs(1).type, '()') - p = p.descend(refs(1).subs); - refs = refs(2:end); - end - - % is that it? - if isempty(refs) - out = p; - return - end - - % next ref must be . - if ~strcmp(refs(1).type, '.') - panel.error('InvalidIndexing'); - end - - % switch on "fieldname" - switch refs(1).subs - - case { ... - 'fontname' 'fontsize' 'fontweight' ... - 'margin' 'marginleft' ... - 'marginbottom' 'marginright' 'margintop' ... - 'units' ... - } - - % delegate this property get - out = p.getPropertyValue(refs(1).subs); - - case 'position' - out = p.getObjectPosition(); - - case 'figure' - out = p.h_figure; - - case 'packspec' - out = p.packspec; - - case 'axis' - if p.isObject() - out = p.getAllManagedAxes(); - else - out = []; - end - - case 'object' - if p.isObject() - h = p.h_object; - ih = ishandle(h); - out = h(ih); - else - out = []; - end - - case {'ch' 'children' 'de' 'descendants' 'fa' 'family'} - - % get the set - switch refs(1).subs - case {'children' 'ch'} - out = {}; - for n = 1:length(p.m_children) - out{n} = p.m_children(n); - end - case {'descendants' 'de'} - out = p.getPanels('*'); - for c = 1:length(out) - if out{c} == p - out = out([1:c-1 c+1:end]); - break - end - end - case {'family' 'fa'} - out = p.getPanels('*'); - end - - % we handle a special case of deeper reference - % here, because we are abusing matlab's syntax to - % do it. other cases (non-abusing) will be handled - % recursively, as usual. this is when we go: - % - % p.ch.axis - % - % which isn't syntactically sound since p.ch is a - % cell array (and potentially a non-singular one - % at that). we re-interpret this to mean - % [p.ch{1}.axis p.ch{2}.axis ...], as follows. - if length(refs) > 1 && isequal(refs(2).type, '.') - switch refs(2).subs - case {'axis' 'object'} - pp = out; - out = []; - for i = 1:length(pp) - out = cat(2, out, subsref(pp{i}, refs(2))); - end - refs = refs(2:end); % used up! - otherwise - % give an informative error message - panel.error('InvalidIndexing'); - end - end - - case { ... - 'addCallback' 'setCallback' 'clearCallbacks' ... - 'hold' ... - 'refresh' 'export' ... - 'pack' 'repack' ... - 'identify' 'show' ... - } - - % validate - if length(refs) ~= 2 || ~strcmp(refs(2).type, '()') - error('panel:InvalidIndexing', ['"' refs(1).subs '" is a function (try "help panel/' refs(1).subs '")']); - end - - % delegate this function call with no output - builtin('subsref', p, refs); - return - - case { ... - 'select' 'fixdash' ... - 'xlabel' 'ylabel' 'zlabel' 'title' ... - 'find' ... - } - - % validate - if length(refs) ~= 2 || ~strcmp(refs(2).type, '()') - error('panel:InvalidIndexing', ['"' refs(1).subs '" is a function (try "help panel/' refs(1).subs '")']); - end - - % delegate this function call with output - if nargout - out = builtin('subsref', p, refs); - else - builtin('subsref', p, refs); - end - return - - otherwise - panel.error('InvalidIndexing'); - - end - - % continue - if length(refs) > 1 - out = subsref(out, refs(2:end)); - end - - end - - end - - - - - - - - - - - - - - %% ---- UTILITY METHODS ---- - - methods (Access = private) - - function b = ismanagefont(p) - - % ask root - b = p.m_root.state.manage_font; - - end - - function b = isdefer(p) - - % ask root - b = p.m_root.state.defer ~= 0; - - end - - function defer(p) - - % increment - p.m_root.state.defer = p.m_root.state.defer + 1; - - end - - function undefer(p) - - % decrement - p.m_root.state.defer = p.m_root.state.defer - 1; - - end - - function cs = getPanels(p, panelTypes, edgespec, all) - - % return all the panels that match the specification. - % - % panelTypes "*": return all panels - % panelTypes "s": return all sizeable panels (parent, - % object and uncommitted) - % panelTypes "p": return only physical panels (object - % or uncommitted) - % panelTypes "o": return only object panels - % - % if edgespec/all is specified, only panels matching - % the edgespec are returned (all of them if "all" is - % true, or any of them - the first one, in fact - if - % "all" is false). - - cs = {}; - - % do not include any that use absolute positioning - - % they stand outside of the sizing model - skip = (numel(p.packspec) == 4) && ~any(panelTypes == '*'); - - if p.isParent() - - % return if appropriate type - if any(panelTypes == '*s') && ~skip - cs = {p}; - end - - % if edgespec was supplied - if nargin == 4 - - % if we are perpendicular to the specified edge - if p.packdim ~= edgespec(1) - - if all - - % return all matching - for c = 1:length(p.m_children) - ppp = p.m_children(c).getPanels(panelTypes, edgespec, all); - cs = cat(2, cs, ppp); - end - - else - - % return only the first one - cs = cat(2, cs, p.m_children(1).getPanels(panelTypes, edgespec, all)); - - end - - else - - % if we are parallel to the specified edge - if edgespec(2) == 2 - - % use last - ppp = p.m_children(end).getPanels(panelTypes, edgespec, all); - cs = cat(2, cs, ppp); - - else - - % use first - cs = cat(2, cs, p.m_children(1).getPanels(panelTypes, edgespec, all)); - - end - - end - - else - - % else, return all - for c = 1:length(p.m_children) - ppp = p.m_children(c).getPanels(panelTypes); - cs = cat(2, cs, ppp); - end - - end - - elseif p.isObject() - - % return if appropriate type - if any(panelTypes == '*spo') && ~skip - cs = {p}; - end - - else - - % return if appropriate type - if any(panelTypes == '*sp') && ~skip - cs = {p}; - end - - end - - end - - function commitAsParent(p) - - if p.isUncommitted() - p.m_panelType = p.PANEL_TYPE_PARENT; - elseif p.isObject() - error('panel:AlreadyCommitted', 'cannot make this panel a parent panel, it is already an object panel'); - end - - end - - function commitAsObject(p) - - if p.isUncommitted() - p.m_panelType = p.PANEL_TYPE_OBJECT; - elseif p.isParent() - error('panel:AlreadyCommitted', 'cannot make this panel an object panel, it is already a parent panel'); - end - - end - - function b = isRoot(p) - - b = isempty(p.parent); - - end - - function b = isParent(p) - - b = p.m_panelType == p.PANEL_TYPE_PARENT; - - end - - function b = isObject(p) - - b = p.m_panelType == p.PANEL_TYPE_OBJECT; - - end - - function b = isUncommitted(p) - - b = p.m_panelType == p.PANEL_TYPE_UNCOMMITTED; - - end - - function h_axes = getAllManagedAxes(p) - - h_axes = []; - for n = 1:length(p.h_object) - h = p.h_object(n); - if isaxis(h) - h_axes = [h_axes h]; - end - end - - end - - function h_object = getOrCreateAxis(p) - - switch p.m_panelType - - case p.PANEL_TYPE_PARENT - - % create if not present - if isempty(p.h_object) - - % 'Visible', 'off' - % this is the hidden axis of a parent panel, - % used for displaying a parent panel's xlabel, - % ylabel and title, but not as a plotting axis - % - % 'NextPlot', 'replacechildren' - % make sure fonts etc. don't get changed when user - % plots into it - p.h_object = axes( ... - 'Parent', p.h_parent, ... - 'Visible', 'off', ... - 'NextPlot', 'replacechildren' ... - ); - - % make sure it's unitary, to help us in - % positioning labels and title - axis(p.h_object, [0 1 0 1]); - - % refresh this axis position - p.applyLayout(); - - end - - % ok - h_object = p.h_object; - - case p.PANEL_TYPE_OBJECT - - % ok - h_object = p.getAllManagedAxes(); - if isempty(h_object) - error('panel:ManagedObjectNotAnAxis', 'this object panel does not manage an axis'); - end - - case p.PANEL_TYPE_UNCOMMITTED - - panel.error('PanelUncommitted'); - - end - - end - - function removeChild(p, child) - - % if not a parent, fail but warn (shouldn't happen) - if ~p.isParent() - warning('panel:NotParentOnRemoveChild', 'i am not a parent (in removeChild())'); - return - end - - % remove from children - for c = 1:length(p.m_children) - if p.m_children(c) == child - p.m_children = p.m_children([1:c-1 c+1:end]); - return - end - end - - % warn - warning('panel:ChildAbsentOnRemoveChild', 'child not found (in removeChild())'); - - end - - function h = getShowAxis(p) - - if p.isRoot() - if isempty(p.h_showAxis) - - % create - p.h_showAxis = axes( ... - 'Parent', p.h_parent, ... - 'units', 'normalized', ... - 'position', [0 0 1 1] ... - ); - - % move to bottom - c = get(p.h_parent, 'children'); - c = [c(2:end); c(1)]; - set(p.h_parent, 'children', c); - - % finalise axis - set(p.h_showAxis, ... - 'xtick', [], 'ytick', [], ... - 'color', 'none', 'box', 'off' ... - ); - axis(p.h_showAxis, [0 1 0 1]); - - % hold - hold(p.h_showAxis, 'on'); - - end - - % return it - h = p.h_showAxis; - - else - h = p.parent.getShowAxis(); - end - - end - - function fireCallbacks(p, event) - - % for each attached callback - for c = 1:length(p.m_callback) - - % extract - callback = p.m_callback{c}; - func = callback{1}; - userdata = callback{2}; - - % fire - data = []; - data.panel = p; - data.event = event; - data.context = p.m_context; - data.userdata = userdata; - func(data); - - end - - end - - end - - - - - - - - - - - - - %% ---- LAYOUT METHODS ---- - - methods - - function refresh(p) - - % recompute layout of all panels - % - % p.refresh() - % recompute the layout of all panels from scratch. - % this should not usually be required, and is - % provided primarily for legacy support. - - % LEGACY - % - % NB: if you pass 'defer' to the constructor, calling - % refresh() both recomputes the layout and releases - % the defer mode. future changes to properties (e.g. - % margins) will cause immediate recomputation of the - % layout, so only call refresh() when you're done. - - % bubble up to root - if ~p.isRoot() - p.m_root.refresh(); - return - end - - % release defer - p.state.defer = 0; - - % debug output -% panel.debugmsg(['refresh "' p.state.name '"...']); - - % call recomputeLayout - p.recomputeLayout([]); - - end - - end - - methods (Access = private) - - function do_fixdash(p, context) - - % if context is [], this is _after_ the layout for - % export, so we need to restore - if isempty(context) - - % restore lines we changed to their original state - for r = 1:length(p.m_fixdash_restore) - - % get - restore = p.m_fixdash_restore{r}; - - % if empty, no change was made - if ~isempty(restore) - set(restore.h_line, ... - 'xdata', restore.xdata, 'ydata', restore.ydata); - delete([restore.h_supp restore.h_mark]); - end - - end - - else - -% % get handles to objects that still exist -% h_lines = p.m_fixdash(ishandle(p.m_fixdash)); - - % no restores - p.m_fixdash_restore = {}; - - % for each line - for i = 1:length(p.m_fixdash) - - % get - fix = p.m_fixdash{i}; - - % final check - if ~ishandle(fix.h) || ~isequal(get(fix.h, 'type'), 'line') - continue - end - - % apply dashstyle - p.m_fixdash_restore{end+1} = dashstyle_line(fix, context); - - end - - end - - end - - function p = recomputeLayout(p, context) - - % this function recomputes the layout from scratch. - % this means calculating the sizes of the root panel - % and all descendant panels. after this is completed, - % the function calls applyLayout to effect the new - % layout. - - % if not root, bubble up to root - if ~p.isRoot() - p.m_root.recomputeLayout(context); - return - end - - % if in defer mode, do not compute layout - if p.isdefer() - return - end - - % if no context supplied (e.g. on resize events), use - % the figure window (a context is supplied if - % exporting to an image file). - if isempty(context) - context.mode = panel.LAYOUT_MODE_NORMAL; - context.size_in_mm = []; - context.rect = [0 0 1 1]; - end - - % debug output -% panel.debugmsg(['recomputeLayout "' p.state.name '"...']); - -% % root may have a packspec of its own -% if ~isempty(p.packspec) -% if isscalar(p.packspec) -% % this should never happen, because it should be -% % caught when the packspec is set in repack() -% warning('panel:RootPanelCannotUseRelativeMode', 'the root panel uses relative positioning mode - this is ignored'); -% else -% context.rect = p.packspec; -% end -% end - - % if not given a context size, use the size on screen - % of the parent figure - if isempty(context.size_in_mm) - - % get context (whole parent) size in its units - pp = get(p.h_figure, 'position'); - context_size = pp(3:4); - - % defaults, in case this fails for any reason - screen_size = [1280 1024]; - if ismac - screen_dpi = 72; - else - screen_dpi = 96; - end - - % get screen DPI - try - local_screen_dpi = get(0, 'ScreenPixelsPerInch'); - if ~isempty(local_screen_dpi) - screen_dpi = local_screen_dpi; - end - end - - % get screen size - try - local_screen_size = get(0, 'ScreenSize'); - if ~isempty(local_screen_size) - screen_size = local_screen_size; - end - end - - % get figure width and height on screen - switch get(p.h_figure, 'Units') - - case 'points' - points_per_inch = 72; - context.size_in_mm = context_size / points_per_inch * 25.4; - - case 'inches' - context.size_in_mm = context_size * 25.4; - - case 'centimeters' - context.size_in_mm = context_size * 10.0; - - case 'pixels' - context.size_in_mm = context_size / screen_dpi * 25.4; - - case 'characters' - context_size = context_size .* [5 13]; % convert to pixels (based on empirical measurement) - context.size_in_mm = context_size / screen_dpi * 25.4; - - case 'normalized' - context_size = context_size .* screen_size(3:4); % convert to pixels (based on screen size) - context.size_in_mm = context_size / screen_dpi * 25.4; - - otherwise - error('panel:CaseNotCoded', ['case not coded, (Parent Units are ' get(p.h_figure, 'Units') ')']); - - end - - end - - % that's the figure size, now we need the size of our - % parent, if it's not the figure too - if p.h_parent ~= p.h_figure - units = get(p.h_parent, 'units'); - set(p.h_parent, 'units', 'normalized'); - pos = get(p.h_parent, 'position'); - set(p.h_parent, 'units', units); - context.size_in_mm = context.size_in_mm .* pos(3:4); - end - - % for the root, we apply the margins here, since it's - % a special case because there's always exactly one of - % it - margin = p.getPropertyValue('margin', 'mm'); - m = margin([1 3]) / context.size_in_mm(1); - context.rect = context.rect + [m(1) 0 -sum(m) 0]; - m = margin([2 4]) / context.size_in_mm(2); - context.rect = context.rect + [0 m(1) 0 -sum(m)]; - - % now, recurse - p.recurseComputeLayout(context); - - % clear h_showAxis when we recompute the layout - if ~isempty(p.h_showAxis) - delete(p.h_showAxis); - p.h_showAxis = []; - end - - % having computed the layout, we now apply it, - % starting at the root panel. - p.applyLayout('recurse'); - - end - - function recurseComputeLayout(p, context) - - % store context - p.m_context = context; - - % if no children, do nothing further - if isempty(p.m_children) - return - end - - % else, we're going to recompute the layout for our - % children - margins = []; - - % get size to pack into - mm_canvas = context.size_in_mm(p.packdim); - mm_context = mm_canvas * context.rect(2+p.packdim); - - % get list of children that are packed relative - we - % do this because the computation only handles these - % relative children; absolute packed children are - % ignored through the computation, and are just packed - % as specified when the time comes. - rel_list = []; - - % for each child - for i = 1:length(p.m_children) - - % get child - c = p.m_children(i); - - % is it packed abs? - if isofsize(c.packspec, [1 4]) - continue - end - - % if not, it's packed relative, so add to list - rel_list(end+1) = i; - - end - - % array of actual sizes as fraction of parent (note we - % only represent the rel_list). - zz = zeros(1, length(rel_list)); - sz_phys = zz; - sz_frac = zz; - i_stretch = zz; - - % for each child that is packed relative - for i = 1:length(rel_list) - - % get child - c = p.m_children(rel_list(i)); - - % get internal margin - margin = c.getPropertyValue('margin', 'mm'); - if p.packdim == 2 - margin = margin([2 4]); - margin = fliplr(margin); % doclink FLIP_PACKDIM_2 - same reason, here! - else - margin = margin([1 3]); - end - margins(i:i+1, i) = margin'; - - % subtract fixed size packspec from packing size - if iscell(c.packspec) - % NB: fixed size is always _stored_ in mm! - sz_phys(i) = c.packspec{1}; - end - - % get relative packing sizes - if isnumeric(c.packspec) && isscalar(c.packspec) - % NB: relative size is a scalar numeric - sz_frac(i) = c.packspec; - % convert perc to frac - if sz_frac(i) > 1 - sz_frac(i) = sz_frac(i) / 100; - end - end - - % get stretch packing size - if isempty(c.packspec) - % NB: these will be filled later - i_stretch(i) = 1; - end - - % else, it's an abs packing size, and we can ignore - % it for this phase of layout - - end - - % finalise internal margins (that is, the margin at - % each boundary between two adjacent relative packed - % panels is the maximum of the margins specified by - % each of the pair). - margins = max(margins, [], 2); - margins = margins(2:end-1)'; - - % subtract internal margins to give available space - % for objects (in mm) - mm_objects = mm_context - sum(margins); - - % now, subtract physically sized objects to give - % available space to share out amongst panels that - % specify their size as a fraction. - mm_share = mm_objects - sum(sz_phys); - - % and now stretch items can be given their actual - % fractional size, since we now know who they are - % sharing space with. - sz_frac(find(i_stretch)) = (1 - sum(sz_frac)) / sum(i_stretch); - - % and we can now get the real physical size of all the - % fractionally-sized panels in mm. - sz_frac = sz_frac * mm_share; - - % finally, we've got the physical boundaries of - % everything; let's just tidy that up. - sz = [[sz_phys + sz_frac]; margins 0]; - sz = sz(1:end-1); - - % and let's normalise the physical boundaries, because - % we're actually going to specify them to matlab in - % normalised form, even though we computed them in mm. - if ~isempty(sz) - - % do it - sz_norm = reshape([0 cumsum(sz / mm_context)]', 2, [])'; - - % for packdim 2, we pack from the top, whereas - % matlab's position property packs from the bottom, so - % we have to flip these. doclink FLIP_PACKDIM_2. - if p.packdim == 2 - sz_norm = fliplr(1 - sz_norm); - end - - end - - % recurse - for i = 1:length(p.m_children) - - % get child - c = p.m_children(i); - - % handle abs packed panels - if isofsize(c.packspec, [1 4]) - - % child context - child_context = context; - rect = child_context.rect; - rect([1 3]) = c.packspec([1 3]) * rect(3) + [rect(1) 0]; - rect([2 4]) = c.packspec([2 4]) * rect(4) + [rect(2) 0]; - child_context.rect = rect; - - else - - % child context - child_context = context; - rr = sz_norm(1, :); - sz_norm = sz_norm(2:end, :); % sz_norm has only as many entries as there are rel-packed panels - ri = p.packdim + [0 2]; - a = child_context.rect(ri(1)); - b = child_context.rect(ri(2)); - child_context.rect(ri) = [a+rr(1)*b diff(rr)*b]; - - end - - % recurse - c.recurseComputeLayout(child_context); - - end - - end - - function applyLayout(p, varargin) - - % this function applies the layout that is stored in - % each panel objects "m_context" member, and fixes up - % the position of any associated objects (such as axis - % group labels). - - % skip if disabled - if p.isdefer() - return - end - - % debug output -% panel.debugmsg(['applyLayout "' p.state.name '"...']); - - % defaults - recurse = false; - - % handle arguments - while ~isempty(varargin) - - % get - arg = varargin{1}; - varargin = varargin(2:end); - - % handle - switch arg - case 'recurse' - recurse = true; - otherwise - panel.error('InternalError'); - end - - end - - % recurse - if recurse - pp = p.getPanels('*'); - else - pp = {p}; - end - - % why do we have to split the applyLayout() operation - % into two? - % - % because the "group labels" are positioned with - % respect to the axes in their group depending on - % whether those axes have tick labels, and what those - % tick labels are. if those tick labels are in - % automatic mode (as they usually are), they may - % change when those axes are positioned. since an axis - % group may contain many of these nested deep, we have - % to position all axes (step 1) first, then (step 2) - % position any group labels. - - % step 1 - for pi = 1:length(pp) - pp{pi}.applyLayout1(); - end - - % step 2 - for pi = 1:length(pp) - pp{pi}.applyLayout2(); - end - - % callbacks - for pi = 1:length(pp) - fireCallbacks(pp{pi}, 'layout-updated'); - end - - end - - function r = getObjectPosition(p) - - % get packed position - r = p.m_context.rect; - - % if empty, must be absolute position - if isempty(r) - r = p.packspec; - pp = getObjectPosition(p.parent); - r = panel.getRectangleOfRectangle(pp, r); - end - - end - - function applyLayout1(p) - - % if no context yet, skip this call - if isempty(p.m_context) - return - end - - % if no managed objects, skip this call - if isempty(p.h_object) - return - end - - % debug output -% panel.debugmsg(['applyLayout1 "' p.state.name '"...']); - - % handle LAYOUT_MODE - switch p.m_context.mode - - case panel.LAYOUT_MODE_PREPRINT - - % if in LAYOUT_MODE_PREPRINT, store current axis - % layout (ticks and ticklabels) and lock them into - % manual mode so they don't get changed during the - % print operation - h_axes = p.getAllManagedAxes(); - for n = 1:length(h_axes) - p.state.store{n} = storeAxisState(h_axes(n)); - end - - case panel.LAYOUT_MODE_POSTPRINT - - % if in LAYOUT_MODE_POSTPRINT, restore axis - % layout, leaving it as it was before we ran - % export - h_axes = p.getAllManagedAxes(); - for n = 1:length(h_axes) - restoreAxisState(h_axes(n), p.state.store{n}); - end - - end - - % position it - try - set(p.h_object, 'position', p.getObjectPosition(), 'units', 'normalized'); - catch err - if strcmp(err.identifier, 'MATLAB:hg:set_chck:DimensionsOutsideRange') - w = warning('query', 'backtrace'); - warning off backtrace - warning('panel:PanelZeroSize', 'a panel had zero size, and the managed object was hidden'); - set(p.h_object, 'position', [-0.3 -0.3 0.2 0.2]); - if strcmp(w.state, 'on') - warning on backtrace - end - elseif strcmp(err.identifier, 'MATLAB:class:InvalidHandle') - % this will happen if the user deletes the managed - % objects manually. an obvious way that this - % happens is if the user select()s some panels so - % that axes get created, then calls clf. it would - % be nice if we could clear the panels attached to - % a figure in response to a clf call, but there - % doesn't seem any obvious way to pick up the clf - % call, only the delete(objects) that follows, and - % this is indistinguishable from a call by the - % user to delete(my_axis), for instance. how are - % we to respond if the user deletes the axis the - % panel is managing? it's not clear. so, we'll - % just fail silently, for now, and these panels - % will either never be used again (and will be - % destroyed when the figure is closed) or will be - % destroyed when the user creates a new panel on - % this figure. either way, i think, no real harm - % done. -% w = warning('query', 'backtrace'); -% warning off backtrace -% warning('panel:PanelObjectDestroyed', 'the object managed by a panel has been destroyed'); -% if strcmp(w.state, 'on') -% warning on backtrace -% end -% panel.debugmsg('***WARNING*** the object managed by a panel has been destroyed'); - return - else - rethrow(err) - end - end - - % if managing fonts - if p.ismanagefont() - - % apply properties to objects - h = p.h_object; - - % get those which are axes - h_axes = p.getAllManagedAxes(); - - % and labels/title objects, for any that are axes - for n = 1:length(h_axes) - h = [h ... - get(h_axes(n), 'xlabel') ... - get(h_axes(n), 'ylabel') ... - get(h_axes(n), 'zlabel') ... - get(h_axes(n), 'title') ... - ]; - end - - % apply font properties - set(h, ... - 'fontname', p.getPropertyValue('fontname'), ... - 'fontsize', p.getPropertyValue('fontsize'), ... - 'fontweight', p.getPropertyValue('fontweight') ... - ); - - end - - end - - function applyLayout2(p) - - % if no context yet, skip this call - if isempty(p.m_context) - return - end - - % if no object, skip this call - if isempty(p.h_object) - return - end - - % if not a parent, skip this call - if ~p.isParent() - return - end - - % if not an axis, skip this call - NB: this is not a - % displayed and managed object, rather it is the - % invisible axis used to display parent labels/titles. - % we checked above if this panel is a parent. thus, - % the member h_object must be scalar, if it is - % non-empty. - if ~isaxis(p.h_object) - return - end - - % debug output -% panel.debugmsg(['applyLayout2 "' p.state.name '"...']); - - % matlab moves x/ylabels around depending on - % whether the axis in question has any x/yticks, - % so that the label is always "near" the axis. - % we try to do the same, but it's hack-o-rama. - - % calibration offsets - i measured these - % empirically, what a load of shit - font_fudge = [2 1/3]; - nofont_fudge = [2 0]; - - % do xlabel - cs = p.getPanels('o', [2 2], true); - y = 0; - for c = 1:length(cs) - ch = cs{c}; - h_axes = ch.getAllManagedAxes(); - for h_axis = h_axes - % only if there are some tick labels, and they're - % at the bottom... - if ~isempty(get(h_axis, 'xticklabel')) && ~isempty(get(h_axis, 'xtick')) ... - && strcmp(get(h_axis, 'xaxislocation'), 'bottom') - fontoffset_mm = get(h_axis, 'fontsize') * font_fudge(2) + font_fudge(1); - y = max(y, fontoffset_mm); - end - end - end - y = max(y, get(p.h_object, 'fontsize') * nofont_fudge(2) + nofont_fudge(1)); - - % convert and lay in - axisheight_mm = p.m_context.size_in_mm(2) * p.m_context.rect(4); - y = y / axisheight_mm; - set(get(p.h_object, 'xlabel'), ... - 'VerticalAlignment', 'Cap', ... - 'Units', 'Normalized', ... - 'Position', [0.5 -y 1]); - - % calibration offsets - i measured these - % empirically, what a load of shit - font_fudge = [3 1/6]; - nofont_fudge = [2 0]; - - % do ylabel - cs = p.getPanels('o', [1 1], true); - x = 0; - for c = 1:length(cs) - ch = cs{c}; - h_axes = ch.getAllManagedAxes(); - for h_axis = h_axes - % only if there are some tick labels, and they're - % at the left... - if ~isempty(get(h_axis, 'yticklabel')) && ~isempty(get(h_axis, 'ytick')) ... - && strcmp(get(h_axis, 'yaxislocation'), 'left') - yt = get(h_axis, 'yticklabel'); - if ischar(yt) - ml = size(yt, 2); - else - ml = 0; - for i = 1:length(yt) - ml = max(ml, length(yt{i})); - end - end - fontoffset_mm = get(h_axis, 'fontsize') * ml * font_fudge(2) + font_fudge(1); - x = max(x, fontoffset_mm); - end - end - end - x = max(x, get(p.h_object, 'fontsize') * nofont_fudge(2) + nofont_fudge(1)); - - % convert and lay in - axisheight_mm = p.m_context.size_in_mm(1) * p.m_context.rect(3); - x = x / axisheight_mm; - set(get(p.h_object, 'ylabel'), ... - 'VerticalAlignment', 'Bottom', ... - 'Units', 'Normalized', ... - 'Position', [-x 0.5 1]); - - % calibration offsets - made up based on the - % ones i measured for the labels - nofont_fudge = [2 0]; - - % get y position - y = max(y, get(p.h_object, 'fontsize') * nofont_fudge(2) + nofont_fudge(1)); - - % convert and lay in - axisheight_mm = p.m_context.size_in_mm(2) * p.m_context.rect(4); - y = y / axisheight_mm; - set(get(p.h_object, 'title'), ... - 'VerticalAlignment', 'Bottom', ... - 'Position', [0.5 1+y 1]); - - % 21/11/19 move to bottom of z-index stack so - % that it does not interfere with mouse - % interactions with the other axes (e.g. - % zooming) - uistack(p.h_object, 'bottom') - - end - - end - - - - - - - %% ---- PROPERTY METHODS ---- - - methods (Access = private) - - function value = getPropertyValue(p, key, units) - - value = p.prop.(key); - - if isempty(value) - - % inherit - if ~isempty(p.parent) - switch key - case {'fontname' 'fontsize' 'fontweight' 'margin' 'units'} - if nargin == 3 - value = p.parent.getPropertyValue(key, units); - else - value = p.parent.getPropertyValue(key); - end - return - end - end - - % default - if isempty(value) - value = panel.getPropertyDefault(key); - end - - end - - % translate dimensions - switch key - case {'margin'} - if nargin < 3 - units = p.getPropertyValue('units'); - end - value = panel.resolveUnits(value, units); - end - - end - - function setPropertyValue(p, key, value) - - % root properties - switch key - case 'units' - if ~isempty(p.parent) - p.parent.setPropertyValue(key, value); - return - end - end - - % value validation - switch key - case 'units' - invalid = ~( (isstring(value) && isin({'mm' 'in' 'cm' 'pt'}, value)) || isempty(value) ); - case 'fontname' - invalid = ~( isstring(value) || isempty(value) ); - case 'fontsize' - invalid = ~( (isnumeric(value) && isscalar(value) && value >= 4 && value <= 60) || isempty(value) ); - case 'fontweight' - invalid = ~( (isstring(value) && isin({'normal' 'bold'}, value)) || isempty(value) ); - case 'margin' - invalid = ~( (isdimension(value)) || isempty(value) ); - case {'marginleft' 'marginbottom' 'marginright' 'margintop'} - invalid = ~isscalardimension(value); - otherwise - error('panel:UnrecognisedProperty', ['unrecognised property "' key '"']); - end - - % value validation - if invalid - error('panel:InvalidValueForProperty', ['invalid value for property "' key '"']); - end - - % marginX properties - switch key - case {'marginleft' 'marginbottom' 'marginright' 'margintop'} - index = isin({'left' 'bottom' 'right' 'top'}, key(7:end)); - element = value; - value = p.getPropertyValue('margin'); - value(index) = element; - key = 'margin'; - end - - % translate dimensions - switch key - case {'margin'} - if isscalar(value) - value = value * [1 1 1 1]; - end - if ~isempty(value) - units = p.getPropertyValue('units'); - value = {panel.resolveUnits({value units}, 'mm') 'mm'}; - end - end - - % lay in - p.prop.(key) = value; - - end - - end - - methods (Static = true, Access = private) - - function s = fignum(h) - - % handled differently pre/post 2014b - if isa(h, 'matlab.ui.Figure') - % R2014b - s = num2str(h.Number); + p.h_parent = passed_h_parent.h_parent; + p.h_figure = passed_h_parent.h_figure; + p.parent = passed_h_parent; + p.m_root = passed_h_parent.m_root; + + % done! + return + + end + + % default condition + passed_h_parent = []; + add = false; + + % peel off args + while ~isempty(varargin) + + % get arg + arg = varargin{1}; + varargin = varargin(2:end); + + % handle text + if ischar(arg) + + switch arg + + case 'add' + add = true; + continue + + case 'defer' + p.state.defer = 1; + continue + + case 'no-manage-font' + p.state.manage_font = 0; + continue + + case {'mm' 'cm' 'in' 'pt'} + p.setPropertyValue('units', arg); + continue + + otherwise + error('panel:InvalidArgument', ['unrecognised text argument "' arg '"']); + + end + + end + + % handle parent + if isscalar(arg) && ishandle(arg) + passed_h_parent = arg; + continue + end + + % error + error('panel:InvalidArgument', 'unrecognised argument to panel constructor'); + + end + + % attach to current figure if no parent supplied + if isempty(passed_h_parent) + passed_h_parent = gcf; + + % this might cause a figure to be created - if so, + % give it time to display now so we don't get a (or + % two, in fact!) resize event(s) later + drawnow; + end + + % we are a root panel + p.state.name = 'root'; + p.parent = []; + p.m_root = p; + + % get parent type + parentType = get(passed_h_parent, 'type'); + + % set handles + switch parentType + + case 'uipanel' + p.h_parent = passed_h_parent; + p.h_figure = getParentFigure(passed_h_parent); + + case 'figure' + p.h_parent = passed_h_parent; + p.h_figure = passed_h_parent; + + otherwise + error('panel:InvalidArgument', ... + ['panel() cannot be attached to an object of type "' parentType '"']); + + end + + % lay in callbacks + addHandleCallback(p.h_figure, 'CloseRequestFcn', @panel.closeCallback); + addHandleCallback(p.h_parent, 'ResizeFcn', @panel.resizeCallback); + + % register for callbacks + if add + panel.callbackDispatcher('registerNoClear', p); + else + panel.callbackDispatcher('register', p); + end + + % lock class in memory (prevent persistent from being cleared by clear all) + panel.lockClass(); + + end + + function delete(p) + + % destroy a panel + % + % delete(p) + % destroy the passed panel, deleting all associated + % graphics objects. + % + % NB: you won't usually have to call this explicitly. + % it is called automatically for all attached panels + % when you close the associated figure. + + % debug output + % panel.debugmsg(['deleting "' p.state.name '"...']); + + % delete managed graphics objects + for n = 1:length(p.h_object) + h = p.h_object(n); + if ishandle(h) + delete(h); + end + end + + % delete associated show axis + if ~isempty(p.h_showAxis) && ishandle(p.h_showAxis) + delete(p.h_showAxis); + end + + % delete all children (child will remove itself from + % "m_children" on delete()) + while ~isempty(p.m_children) + delete(p.m_children(end)); + end + + % unregister... + if p.isRoot() + + % ...for callbacks + panel.callbackDispatcher('unregister', p); + + else + + % ...from parent + p.parent.removeChild(p); + + end + + % debug output + % panel.debugmsg(['deleted "' p.state.name '"!']); + + end + + end + + %% ---- PUBLIC DISPLAY ---- + + methods (Hidden = true) + + function disp(p) + + display(p); + + end + + function display(p, label) + + if nargin == 2 + disp([10 label ' =' 10]); + end + + display_sub(p); + + disp(' '); + + end + + function display_sub(p, indent) + + % default + if nargin < 2 + indent = ''; + end + + % handle non-scalar (should not exist!) + nels = numel(p); + if nels > 1 + sz = size(p); + sz = sprintf('%dx', sz); + disp([sz(1:end - 1) ' array of panel objects']); + return + end + + % header + header = indent; + if p.isObject() + header = [header 'Object ' p.state.name ': ']; + elseif p.isParent() + header = [header 'Parent ' p.state.name ': ']; + else + header = [header 'Uncommitted ' p.state.name ': ']; + end + if p.isRoot() + pp = ['attached to Figure ' panel.fignum(p.h_figure)]; + else + if isempty(p.packspec) + pp = 'stretch'; + elseif iscell(p.packspec) + units = p.getPropertyValue('units'); + val = panel.resolveUnits({p.packspec{1} 'mm'}, units); + pp = sprintf('%.1f%s', val, units); + elseif isscalar(p.packspec) + if p.packspec > 1 + pp = sprintf('%.0f%%', p.packspec); + else + pp = sprintf('%.3f', p.packspec); + end + else + pp = sprintf('%.3f ', p.packspec); + pp = pp(1:end - 1); + end + end + header = [header '[' pp]; + if p.isParent() + edges = {'hori' 'vert'}; + header = [header ', ' edges{p.packdim}]; + end + header = [header ']']; + + % margin + header = rpad(header, 60); + header = [header '[ margin ' sprintf('%.3g ', p.getPropertyValue('margin')) p.getPropertyValue('units') ']']; + + % % index + % if isfield(p.state, 'index') + % header = [header ' (' int2str(p.state.index) ')']; + % end + + % display + disp(header); + + % children + for c = 1:length(p.m_children) + p.m_children(c).display_sub([indent ' ']); + end + + end + + end + + %% ---- PUBLIC METHODS ---- + + methods + + function h = xlabel(p, text) + + % apply an xlabel to the panel (or group) + % + % p.xlabel(...) + % behaves just like xlabel() at the prompt (you can + % use that as an alternative) when called on an axis + % panel. when called on a parent panel, however, the + % group of objects within that parent have a label + % applied. when called on a non-axis object panel, + % an error is raised. + + h = get(p.getOrCreateAxis(), 'xlabel'); + set(h, 'string', text); + if p.isParent() + set(h, 'visible', 'on'); + end + + end + + function h = ylabel(p, text) + + % apply a ylabel to the panel (or group) + % + % p.ylabel(...) + % behaves just like ylabel() at the prompt (you can + % use that as an alternative) when called on an axis + % panel. when called on a parent panel, however, the + % group of objects within that parent have a label + % applied. when called on a non-axis object panel, + % an error is raised. + + h = get(p.getOrCreateAxis(), 'ylabel'); + set(h, 'string', text); + if p.isParent() + set(h, 'visible', 'on'); + end + + end + + function h = zlabel(p, text) + + % apply a zlabel to the panel (or group) + % + % p.zlabel(...) + % behaves just like zlabel() at the prompt (you can + % use that as an alternative) when called on an axis + % panel. when called on a parent panel, however, + % this method raises an error, since parent panels + % are assumed to be 2D, with respect to axes. + + if p.isParent() + error('panel:ZLabelOnParentAxis', 'can only call zlabel() on an object panel'); + end + + h = get(p.getOrCreateAxis(), 'zlabel'); + set(h, 'string', text); + + end + + function h = title(p, text) + + % apply a title to the panel (or group) + % + % p.title(...) + % behaves just like title() at the prompt (you can + % use that as an alternative) when called on an axis + % panel. when called on a parent panel, however, the + % group of objects within that parent have a title + % applied. when called on a non-axis object panel, + % an error is raised. + + h = title(p.getOrCreateAxis(), text); + if p.isParent() + set(h, 'visible', 'on'); + end + + end + + function hold(p, state) + + % set the hold on/off state of the associated axis + % + % p.hold('on' / 'off') + % you can use matlab's "hold" function with plots in + % panel, just like any other plot. there is, + % however, a very minor gotcha that is somewhat + % unlikely to ever come up, but for completeness + % this is the problem and the solutions: + % + % if you create a panel "p", change its font using + % panel, e.g. "p.fontname = 'Courier New'", then call + % "hold on", then "hold off", then plot into it, the + % font is not respected. this situation is unlikely to + % arise because there's usually no reason to call + % "hold off" on a plot. however, there are three + % solutions to get round it, if it does: + % + % a) call p.refresh() when you're finished, to + % update all fonts to managed values. + % + % b) if you're going to call p.export() anyway, + % fonts will get updated when you do. + % + % c) if for some reason you can't do (a) OR (b) (I + % can't think why), you can use the hold() function + % provided by panel instead of that provided by + % Matlab. this will not affect your fonts. for + % example, call "p(2).hold('on')". + + % because the matlab "hold off" command sets an axis's + % nextplot state to "replace", we lose control over + % the axis properties (such as fontname). we set + % nextplot to "replacechildren" when we create an + % axis, but if the user does a "hold on, hold off" + % cycle, we lose that. therefore, we offer this + % alternative. + + % check + if ~p.isObject() + error('panel:HoldWhenNotObjectPanel', 'can only call hold() on an object panel'); + end + + % check + h_axes = p.getAllManagedAxes(); + if isempty(h_axes) + error('panel:HoldWhenNoAxes', 'can only call hold() on a panel that manages one or more axes'); + end + + % switch + switch state + case {'on' true 1} + set(h_axes, 'nextplot', 'add'); + case {'off' false 0} + set(h_axes, 'nextplot', 'replacechildren'); + otherwise + error('panel:InvalidArgument', 'argument to hold() must be ''on'', ''off'', or boolean'); + end + + end + + function fixdash(p, hs, linestyle) + + % pass dashed lines to be fixed up during export + % + % NB: Matlab's difficulty with dotted/dashed lines on export + % seems to be fixed in R2014b, so if using this version or a + % later one, this functionality of panel will be of no + % interest. Text below was from pre R2014b. + % + % p.fixdash(h, linestyle) + % add the lines specified as handles in "h" to the + % list of lines to be "fixed up" during export. + % panel will attempt to get the lines to look right + % during export to all formats where they would + % usually get mussed up. see demopanelI for an + % example of how it works. + % + % the above is the usual usage of fixdash(), but + % you can get more control over linestyle by + % specifying the additional argument, "linestyle". + % if "linestyle" is supplied, it is used as the + % linestyle; if not, the current linestyle of the + % line (-, --, -., :) is used. "linestyle" can + % either be a text string or a series of numbers, as + % described below. + % + % '-' solid + % '--' dashed, equal to [2 0.75] + % '-.' dash-dot, equal to [2 0.75 0.5 0.75] + % ':', '.' dotted, equal to [0.5 0.5] + % + % a number series should be 1xN, where N is a + % multiple of 2, as in the examples above, and + % specifies the lengths of any number of dash + % components that are used before being repeated. + % for instance, '-.' generates a 2 unit segment + % (dash), a 0.75 unit gap, then a 0.5 unit segment + % (dot) and a final 0.75 unit gap. at present, the + % units are always millimetres. this system is + % extensible, so that the following examples are + % also valid: + % + % '--..' dash-dash-dot-dot + % '-..-.' dash-dot-dot-dash-dot + % [2 1 4 1 6 1] 2 dash, 4 dash, 6 dash + + % default + if nargin < 3 + linestyle = []; + end + + % bubble up to root + if ~p.isRoot() + p.m_root.fixdash(hs, linestyle); + return + end + + % for each passed handle + for h = hs(:)' + + % check it's still a handle + if ~ishandle(h) + continue + end + + % check it's a line + if ~isequal(get(h, 'type'), 'line') + continue + end + + % update if in list + found = false; + for i = 1:length(p.m_fixdash) + if h == p.m_fixdash{i}.h + p.m_fixdash{i}.linestyle = linestyle; + found = true; + break + end + end + + % else add to list + if ~found + p.m_fixdash{end + 1} = struct('h', h, 'linestyle', linestyle); + end + + end + + end + + function show(p) + + % highlight the outline of the panel + % + % p.show() + % make the outline of the panel "p" show up in red + % in the figure window. this is useful for + % understanding a complex layout. + % + % see also: identify() + + r = p.getObjectPosition(); + + if ~isempty(r) + h = p.getShowAxis(); + delete(get(h, 'children')); + xdata = [r(1) r(1) + r(3) r(1) + r(3) r(1) r(1)]; + ydata = [r(2) r(2) r(2) + r(4) r(2) + r(4) r(2)]; + plot(h, xdata, ydata, 'r-', 'linewidth', 5); + axis([0 1 0 1]); + end + + end + + function export(p, varargin) + + % to export the root panel to an image file + % + % p.export(filename, ...) + % + % export the figure containing panel "p" to an image file. + % you must supply the filename of this output file, with or + % without a file extension. any further arguments must be + % option strings starting with the dash ("-") character. "p" + % should be the root panel. + % + % if the filename does not include an extension, the + % appropriate extension will be added. if it does, the + % output format will be inferred from it, unless overridden + % by the "-o" option, described below. + % + % if you are targeting a print publication, you may find it + % easiest to size your output using the "paper sizing model" + % (below). if you prefer, you can use the "direct sizing + % model", instead. these two sizing models are described + % below. underneath these are listed the options unrelated + % to sizing (which apply regardless of which sizing model + % you use). + % + % + % + % PAPER SIZING MODEL: + % + % using the paper sizing model, you specify your target as a + % region of a piece of paper, and the actual size in + % millimeters is calculated for you. this is usually very + % convenient, but if you find it unsuitable, the direct + % sizing model (next section) is provided as an alternative. + % + % to specify the region, you specify the type (size) of + % paper, the orientation, the number of columns, and the + % aspect ratio of the figure (or the fraction of a column to + % fill). usually, the remaining options can be left as + % defaults. + % + % -pX + % X is the paper type, A2-A6 or letter (default is A4). + % NB: you can also specify paper type LNCS (Lecture Notes + % in Computer Science), using "-pLNCS". If you do this, + % the margins are also adjusted to match LNCS format. + % + % -l + % specify landscape mode (default is portrait). + % + % -mX + % X is the paper margins in mm. you can provide a scalar + % (same margins all round) or a comma-separated list of + % four values, specifying the left, bottom, right, top + % margins separately (default is 20mm all round). + % + % -iX + % X is the inter-column space in mm (default is + % 5mm). + % + % -cX + % X is the number of columns (default is 1). + % + % NB: the following two options represent two ways to + % specify the height of the figure relative to the space + % defined by the above options. if you supply both, + % whichever comes second will be used. + % + % -aX + % X is the aspect ratio of the resulting image file (width + % is set by the paper model). X can be one of the strings: + % s (square), g (landscape golden ratio), gp (portrait + % golden ratio), h (half-height), d (double-height); or, a + % number greater than zero, to specify the aspect ratio + % explicitly. note that, if using the numeric form, the + % ratio is expressed as the quotient of width over height, + % in the usual way. ratios greater than 10 or less than + % 0.1 are disallowed, since these can cause a very large + % figure file to be created accidentally. default is to + % use the landscape golden ratio. + % + % -fX + % X is the fraction of the column (or page, if there are + % not columns) to fill. X can be one of the following + % strings - a (all), tt (two thirds), h (half), t (third), + % q (quarter) - or a fraction between 0 and 1, to specify + % the fraction of the space to fill as a number. default + % is to use aspect ratio, not fill fraction. + % + % + % + % DIRECT SIZING MODEL: + % + % if one of these two options is set, the output image is + % sized according to that option and the aspect ratio (see + % above) and the paper model is not used. if both are set, + % the aspect ratio is not used either. + % + % -wX + % X is the explicit width in mm. + % + % -hX + % X is the explicit height in mm. + % + % + % + % NON-SIZING OPTIONS: + % + % finally, a few options are provided to control how + % the prepared figure is exported. note that DPI below + % 150 is only recommended for sizing drafts, since + % font and line sizes are not rendered even vaguely + % accurately in some cases. at the other end, DPI + % above 600 is unlikely to be useful except when + % submitting camera-ready copy. + % + % -rX + % X is the resolution (DPI) at which to produce the + % output file. X can be one of the following strings + % - d (draft, 75DPI), n (normal, 150DPI), h (high, + % 300DPI), p (publication quality, 600DPI), x + % (extremely high quality, 1200DPI) - or just + % the DPI as a number (must be in 75-2400). the + % default depends on the output format (see below). + % + % -rX/S + % X is the DPI, S is the smoothing factor, which can + % be 2 or 4. the output file is produced at S times + % the specified DPI, and then reduced in size to the + % specified DPI by averaging. thus, the hard edges + % produced by the renderer are smoothed - the effect + % is somewhat like "anti-aliasing". + % + % NB: the DPI setting might be expected to have no + % effect on vector formats. this is true for SVG, but + % not for EPS, where the DPI affects the numerical + % precision used as well as the size of some image + % elements, but has little effect on file size. for + % this reason, the default DPI is 150 for bitmap + % formats but 600 for vector formats. + % + % -s + % print sideways (default is to print upright) + % + % -oX + % X is the output format - choose from most of the + % built-in image device drivers supported by "print" + % (try "help print"). this includes "png", "jpg", + % "tif", "eps" and "pdf". note that "eps"/"ps" + % resolve to "epsc2"/"psc2", for convenience. to use + % the "eps"/"ps" devices, use "-oeps!"/"-ops!". you + % may also specify "svg", if you have the tool + % "plot2svg" on your path (available at Matlab + % Central File Exchange). the default output format + % is inferred from the file extension, or "png" if + % the passed filename has no extension. + % + % + % + % EXAMPLES: + % + % default export of 'myfig', creates 'myfig.png' at a + % size of 170x105mm (1004x620px). this size comes + % from: A4 (210mm wide), minus two 20mm margins + % (170mm), and using the golden aspect ratio to give a + % height of 105mm, and finally 150DPI to give the + % pixel size. + % + % p.export('myfig') + % + % when producing the final camera-ready image for a + % square figure that will sit in one of the two + % columns of a letter-size paper journal with default + % margins and inter-column space, we might use this: + % + % p.export('myfig', '-pletter', '-c2', '-as', '-rp'); + + % LEGACY + % + % (this is legacy since the 'defer' flag is no longer + % needed - though it is still supported) + % + % NB: if you pass 'defer' to the constructor, calling + % export() both exports the panel and releases the + % defer mode. future changes to properties (e.g. + % margins) will cause immediate recomputation of the + % layout. + + % check + if ~p.isRoot() + error('panel:ExportWhenNotRoot', 'cannot export() this panel - it is not the root panel'); + end + + % used below + default_margin = 20; + + % parameters + pars = []; + pars.filename = ''; + pars.fmt = ''; + pars.ext = ''; + pars.dpi = []; + pars.smooth = 1; + pars.paper = 'A4'; + pars.landscape = false; + pars.fill = -1.618; + pars.cols = 1; + pars.intercolumnspacing = 5; + pars.margin = default_margin; + pars.sideways = false; + pars.width = 0; + pars.height = 0; + invalid = false; + + % interpret args + for a = 1:length(varargin) + + % extract + arg = varargin{a}; + + % all arguments must be non-empty strings + if ~isstring(arg) + error('panel:InvalidArgument', ... + 'all arguments to export() must be non-empty strings'); + end + + % is filename? + if arg(1) ~= '-' + + % error if already set + if ~isempty(pars.filename) + error('panel:InvalidArgument', ... + ['at argument "' arg '", the filename is already set ("' pars.filename '")']); + end + + % ok, continue + pars.filename = arg; + continue + + end + + % split off option key and option value + if length(arg) < 2 + error('panel:InvalidArgument', ... + ['at argument "' arg '", no option specified']); + end + key = arg(2); + val = arg(3:end); + + % switch on option key + switch key + + case 'p' + pars.paper = validate_par(val, arg, {'A2' 'A3' 'A4' 'A5' 'A6' 'letter' 'LNCS'}); + + case 'l' + pars.landscape = true; + validate_par(val, arg, 'empty'); + + case 'm' + pars.margin = validate_par(str2num(val), arg, 'dimension', 'nonneg'); + + case 'i' + pars.intercolumnspacing = validate_par(str2num(val), arg, 'scalar', 'nonneg'); + + case 'c' + pars.cols = validate_par(str2num(val), arg, 'scalar', 'integer'); + + case 'f' + switch val + case 'a' + pars.fill = 1; % all + case 'w' + pars.fill = 1; % whole (legacy, not documented) + case 'tt' + pars.fill = 2 / 3; % two thirds + case 'h' + pars.fill = 1 / 2; % half + case 't' + pars.fill = 1 / 3; % third + case 'q' + pars.fill = 1 / 4; % quarter + otherwise + pars.fill = validate_par(str2num(val), arg, 'scalar', [0 1]); + end + + case 'a' + switch val + case 's' + pars.fill = -1; % square + case 'g' + pars.fill = -1.618; % golden ratio (landscape) + case 'gp' + pars.fill = -1 / 1.618; % golden ratio (portrait) + case 'h' + pars.fill = -2; % half height + case 'd' + pars.fill = -0.5; % double height + otherwise + pars.fill = -validate_par(str2num(val), arg, 'scalar', [0.1 10]); + end + + case 'w' + pars.width = validate_par(str2num(val), arg, 'scalar', 'nonneg', [10 Inf]); + + case 'h' + pars.height = validate_par(str2num(val), arg, 'scalar', 'nonneg', [10 Inf]); + + case 'r' + % peel off smoothing ("/...") + if any(val == '/') + f = find(val == '/', 1); + switch val(f + 1:end) + case '2' + pars.smooth = 2; + case '4' + pars.smooth = 4; + otherwise + error('panel:InvalidArgument', ... + ['invalid argument "' arg '", part after / must be "2" or "4"']); + end + val = val(1:end - 2); + end + + switch val + case 'd' + pars.dpi = 75; % draft + case 'n' + pars.dpi = 150; % normal + case 'h' + pars.dpi = 300; % high + case 'p' + pars.dpi = 600; % publication quality + case 'x' + pars.dpi = 1200; % extremely high quality + otherwise + pars.dpi = validate_par(str2num(val), arg, 'scalar', [75 2400]); + end + + case 's' + pars.sideways = true; + validate_par(val, arg, 'empty'); + + case 'o' + fmts = { + 'png' 'png' 'png' + 'tif' 'tiff' 'tif' + 'tiff' 'tiff' 'tif' + 'jpg' 'jpeg' 'jpg' + 'jpeg' 'jpeg' 'jpg' + 'ps' 'psc2' 'ps' + 'ps!' 'psc' 'ps' + 'psc' 'psc' 'ps' + 'ps2' 'ps2' 'ps' + 'psc2' 'psc2' 'ps' + 'eps' 'epsc2' 'eps' + 'eps!' 'eps' 'eps' + 'epsc' 'epsc' 'eps' + 'eps2' 'eps2' 'eps' + 'epsc2' 'epsc2' 'eps' + 'pdf' 'pdf' 'pdf' + 'svg' 'svg' 'svg' + }; + validate_par(val, arg, fmts(:, 1)'); + index = isin(fmts(:, 1), val); + pars.fmt = fmts(index, 2:3); + + otherwise + error('panel:InvalidArgument', ... + ['invalid argument "' arg '", option is not recognised']); + + end + + end + + % if not specified, infer format from filename + if isempty(pars.fmt) + [path, base, ext] = fileparts(pars.filename); + if ~isempty(ext) + ext = ext(2:end); + end + switch ext + case {'tif' 'tiff'} + pars.fmt = {'tiff' 'tif'}; + case {'jpg' 'jpeg'} + pars.fmt = {'jpeg' 'jpg'}; + case 'eps' + pars.fmt = {'epsc2' 'eps'}; + case {'png' 'pdf' 'svg'} + pars.fmt = {ext ext}; + case '' + pars.fmt = {'png' 'png'}; + otherwise + warning('panel:CannotInferImageFormat', ... + ['unable to infer image format from file extension "' ext '" (PNG assumed)']); + pars.fmt = {'png' 'png'}; + end + end + + % extract + pars.ext = pars.fmt{2}; + pars.fmt = pars.fmt{1}; + + % extract + is_bitmap = ismember(pars.fmt, {'png' 'jpeg' 'tiff'}); + + % default DPI + if isempty(pars.dpi) + if is_bitmap + pars.dpi = 150; + else + pars.dpi = 600; + end + end + + % validate + if isequal(pars.fmt, 'svg') && isempty(which('plot2svg')) + error('panel:Plot2SVGMissing', 'export to SVG requires plot2svg (Matlab Central File Exchange)'); + end + + % validate + if ~is_bitmap && pars.smooth ~= 1 + pars.smooth = 1; + warning('panel:NoSmoothVectorFormat', 'requested smoothing will not be performed (chosen export format is not a bitmap format)'); + end + + % validate + if isempty(pars.filename) + error('panel:InvalidArgument', 'filename not supplied'); + end + + % make sure filename has extension + if ~any(pars.filename == '.') + pars.filename = [pars.filename '.' pars.ext]; + end + + %%%% GET TARGET DIMENSIONS (BEGIN) + + % get space for figure + switch pars.paper + case 'A0' + sz = [841 1189]; + case 'A1' + sz = [594 841]; + case 'A2' + sz = [420 594]; + case 'A3' + sz = [297 420]; + case 'A4' + sz = [210 297]; + case 'A5' + sz = [148 210]; + case 'A6' + sz = [105 148]; + case 'letter' + sz = [216 279]; + case 'LNCS' + sz = [152 235]; + % if margin is still at default, set it to LNCS + % margin size + if isequal(pars.margin, default_margin) + pars.margin = [15 22 15 20]; + end + otherwise + error(['unrecognised paper size "' pars.paper '"']); + end + + % orientation of paper + if pars.landscape + sz = sz([2 1]); + end + + % paper margins (scalar or quad) + if isscalar(pars.margin) + pars.margin = pars.margin * [1 1 1 1]; + end + sz = sz - pars.margin(1:2) - pars.margin(3:4); + + % divide by columns + w = (sz(1) + pars.intercolumnspacing) / pars.cols - pars.intercolumnspacing; + sz(1) = w; + + % apply fill / aspect ratio + if pars.fill > 0 + % fill fraction + sz(2) = sz(2) * pars.fill; + elseif pars.fill < 0 + % aspect ratio + sz(2) = sz(1) * (-1 / pars.fill); + end + + % direct sizing model is used if either of width or height + % is set + if pars.width || pars.height + + % use aspect ratio to fill in either one that is not + % specified + if ~pars.width || ~pars.height + + % aspect ratio must not be a fill + if pars.fill >= 0 + error('cannot use fill fraction with direct sizing model'); + end + + % compute width + if ~pars.width + pars.width = pars.height * -pars.fill; + end + + % compute height + if ~pars.height + pars.height = pars.width / -pars.fill; + end + + end + + % store + sz = [pars.width pars.height]; + + end + + %%%% GET TARGET DIMENSIONS (END) + + % orientation of figure is upright, unless printing + % sideways, in which case the printing space is rotated too + if pars.sideways + set(p.h_figure, 'PaperOrientation', 'landscape'); + sz = fliplr(sz); + else + set(p.h_figure, 'PaperOrientation', 'portrait'); + end + + % report export size + msg = ['exporting to ' int2str(sz(1)) 'x' int2str(sz(2)) 'mm']; + if is_bitmap + psz = sz / 25.4 * pars.dpi; + msg = [msg ' (' int2str(psz(1)) 'x' int2str(psz(2)) 'px @ ' int2str(pars.dpi) 'DPI)']; + else + msg = [msg ' (vector format @ ' int2str(pars.dpi) 'DPI)']; + end + disp(msg); + + % if we are in defer state, we need to do a clean + % recompute first so that axes get positioned so that + % axis ticks get set correctly (if they are in + % automatic mode), since the LAYOUT_MODE_PREPRINT + % recompute will store the tick states. + if p.state.defer + p.state.defer = 0; + p.recomputeLayout([]); + end + + % turn off defer, if it is on + p.state.defer = 0; + + % do a pre-print layout + context.mode = panel.LAYOUT_MODE_PREPRINT; + context.size_in_mm = sz; + context.rect = [0 0 1 1]; + p.recomputeLayout(context); + + % need also to disable the warning that we should set + % PaperPositionMode to auto during this operation - + % we're setting it explicitly. + w = warning('off', 'MATLAB:Print:CustomResizeFcnInPrint'); + + % handle smoothing + pars.write_dpi = pars.dpi; + if pars.smooth > 1 + pars.write_dpi = pars.write_dpi * pars.smooth; + print_filename = [pars.filename '-temp']; + else + print_filename = pars.filename; + end + + % disable layout so it doesn't get computed during any + % figure resize operations that occur during printing. + p.state.defer = 1; + + % set size of figure now. it's important we do this + % after the pre-print layout, because in SVG export + % mode the on-screen figure size is changed and that + % would otherwise affect ticks and limits. + switch pars.fmt + + case 'svg' + % plot2svg (our current SVG export mechanism) uses + % 'Units' and 'Position' (i.e. on-screen position) + % rather than the Paper- prefixed ones used by the + % Matlab export functions. + + % store old on-screen position + svg_units = get(p.h_figure, 'Units'); + svg_pos = get(p.h_figure, 'Position'); + + % update on-screen position + set(p.h_figure, 'Units', 'centimeters'); + pos = get(p.h_figure, 'Position'); + pos(3:4) = sz / 10; + set(p.h_figure, 'Position', pos); + + otherwise + set(p.h_figure, ... + 'PaperUnits', 'centimeters', ... + 'PaperPosition', [0 0 sz] / 10, ... + 'PaperSize', sz / 10 ... % * 1.5 / 10 ... % CHANGED 21/06/2011 so that -opdf works correctly - why was this * 1.5, anyway? presumably was spurious... + ); + + end + + % do fixdash (not for SVG, since plot2svg does a nice + % job of dashed lines without our meddling...) + if ~isequal(pars.fmt, 'svg') + p.do_fixdash(context); + end + + % do the export + switch pars.fmt + case 'svg' + plot2svg(print_filename, p.h_figure); + otherwise + print(p.h_figure, '-loose', ['-d' pars.fmt], ['-r' int2str(pars.write_dpi)], print_filename); + end + + % undo fixdash + if ~isequal(pars.fmt, 'svg') + p.do_fixdash([]); + end + + % set on-screen figure size back to what it was, if it + % was changed. + switch pars.fmt + case 'svg' + set(p.h_figure, 'Units', svg_units); + set(p.h_figure, 'Position', svg_pos); + end + + % enable layout again (it was disabled, above, during + % printing). + p.state.defer = 0; + + % enable warnings + warning(w); + + % do a post-print layout + context.mode = panel.LAYOUT_MODE_POSTPRINT; + context.size_in_mm = []; + context.rect = [0 0 1 1]; + p.recomputeLayout(context); + + % handle smoothing + if pars.smooth > 1 + psz = sz * pars.smooth / 25.4 * pars.dpi; + msg = [' (reducing from ' int2str(psz(1)) 'x' int2str(psz(2)) 'px)']; + disp(['smoothing by factor ' int2str(pars.smooth) msg]); + im1 = imread(print_filename); + delete(print_filename); + sz = size(im1); + sz = [sz(1) - mod(sz(1), pars.smooth) sz(2) - mod(sz(2), pars.smooth)] / pars.smooth; + im = zeros(sz(1), sz(2), 3); + mm = 1:pars.smooth:(sz(1) * pars.smooth); + nn = 1:pars.smooth:(sz(2) * pars.smooth); + for m = 0:pars.smooth - 1 + for n = 0:pars.smooth - 1 + im = im + double(im1(mm + m, nn + n, :)); + end + end + im = uint8(im / (pars.smooth^2)); + + % set the DPI correctly in the new file + switch pars.fmt + case 'png' + dpm = pars.dpi / 25.4 * 1000; + imwrite(im, pars.filename, ... + 'XResolution', dpm, ... + 'YResolution', dpm, ... + 'ResolutionUnit', 'meter'); + case 'tiff' + imwrite(im, pars.filename, ... + 'Resolution', pars.dpi * [1 1]); + otherwise + imwrite(im, pars.filename); + end + end + + end + + function clearCallbacks(p) + + % clear all callback functions for the panel + % + % p.clearCallbacks() + p.m_callback = {}; + + end + + function setCallback(p, func, userdata) + + % set the callback function for the panel + % + % p.setCallback(myCallbackFunction, userdata) + % + % NB: this function clears all current callbacks, then + % calls addCallback(myCallbackFunction, userdata). + p.clearCallbacks(); + p.addCallback(func, userdata); + + end + + function addCallback(p, func, userdata) + + % attach a callback function to receive panel events + % + % p.addCallback(myCallbackFunction, userdata) + % register myCallbackFunction() to be called when + % events occur on the panel. at present, the only + % event is "layout-updated", which usually occurs + % after the figure is resized. myCallbackFunction() + % should accept one argument, "data", which will + % have the following fields. + % + % "userdata": the userdata passed to this function, if + % any was supplied, else empty. + % + % "panel": a reference to the panel on which the + % callback was set. this object can be queried in + % the usual way. + % + % "event": name of event (currently only + % "layout-updated"). + % + % "context": the layout context for the panel. this + % includes a field "size_in_mm" which is the + % physical size of the rendering surface (screen + % real estate, or image file) and "rect" which is + % the relative size of the rectangle within that + % occupied by the panel which is the context of + % the callback (in [left, bottom, width, height] + % format). + + invalid = ~isscalar(func) || ~isa(func, 'function_handle'); + if invalid + error('panel:InvalidArgument', 'argument to callback() must be a function handle'); + end + if nargin == 2 + p.m_callback{end + 1} = {func []}; + else + p.m_callback{end + 1} = {func userdata}; + end + + end + + function q = find(p, varargin) + + % find panel according to some search conditions + % + % p.find(...) + % you can use this to recover the panel + % associated with a particular graphics + % object, for example. conditions are + % specified as {type, data} pairs, as listed + % below. + % + % {'object', h} + % returned panels must be managing the object + % "h". + % + % example: + % q = p.find({'object', h_axis}) + + % get all panels + f = p.getPanels('*'); + + % return value + q = {}; + + % search + for i = 1:length(f) + + % get panel + p = f{i}; + + % check conditions + for c = 1:length(varargin) + + % get condition + cond = varargin{c}; + + % switch on type + switch cond{1} + + case 'object' + h = cond{2}; + if ~any(h == p.h_object) + p = []; + end + + otherwise + error(['unrecognised condition type "' cond{1} '"']); + + end + + end + + % if still there + if ~isempty(p) + q{end + 1} = p; + end + + end + + end + + function identify(p) + + % add annotations to help identify individual panels + % + % p.identify() + % when creating a complex layout, it can become + % confusing as to which panel is which. this + % function adds a text label to each axis panel + % indicating how to reference the axis panel through + % the root panel. for instance, if "(2, 3)" is + % indicated, you can find that panel at p(2, 3). + % + % see also: show() + + if p.isObject() + + % get managed axes + h_axes = p.getAllManagedAxes(); + + % if no axes, ignore + if isempty(h_axes) + return + end + + % mark first axis + h_axes = h_axes(1); + cla(h_axes); + text(0.5, 0.5, p.state.name, 'fontsize', 12, 'hori', 'center', 'parent', h_axes); + axis(h_axes, [0 1 0 1]); + grid(h_axes, 'off'); + + else + + % recurse + for c = 1:length(p.m_children) + p.m_children(c).identify(); + end + + end + + end + + function repack(p, packspec) + + % change the packing specifier for an existing panel + % + % p.repack(packspec) + % repack() is a convenience function provided to + % allow easy development of a layout from the + % command prompt. packspec can be any packing + % specifier accepted by pack(). + % + % see also: pack() + + % deny repack() on root + if p.isRoot() + + % let's deny this. I'm not sure it makes anyway. you + % could always pack into root with a panel with + % absolute positioning, so let's deny first, and + % allow later if we're sure it's a good idea. + error('panel:InvalidArgument', 'root panel cannot be repack()ed'); + + end + + % validate + validate_packspec(packspec); + + % handle units + if iscell(packspec) + units = p.getPropertyValue('units'); + packspec{1} = panel.resolveUnits({packspec{1} units}, 'mm'); + end + + % update the packspec + p.packspec = packspec; + + % and recomputeLayout + p.recomputeLayout([]); + + end + + function pack(p, varargin) + + % add (pack) child panel(s) into an existing panel + % + % p.pack(...) + % add children to the panel "p", committing it as a + % "parent" panel (if it is not already). new (child) + % panels are created using this call - they start as + % "uncommitted" panels. if the parent already has + % children, the new children are appended. The + % following arguments are understood: + % + % 'h'/'v' - switch "p" to pack in the horizontal or + % vertical packing dimension for relative packing + % mode (default for new panels is vertical). + % + % {a, b, c, ...} (a cell row vector) - pack panels + % into "p" with "packing specifiers" a, b, c, etc. + % packing specifiers are detailed below. + % + % PACKING MODES + % panels can be packed into their parent in two + % modes, dependent on their packing specifier. you + % can see a visual representation of these modes on + % the HTML documentation page "Layout". + % + % (i) Relative Mode - panels are packed into the space + % occupied by their parent. size along the parent's + % "packing dimension" is dictated by the packing + % specifier; along the other dimension size matches + % the parent. the following packing specifiers + % indicate Relative Mode. + % + % a) Fixed Size: the specifier is a scalar double in + % a cell {d}. The panel will be of size d in the + % current units of "p" (see the property "p.units" + % for details, but default units are mm). + % + % b) Fractional Size: the specifier is a scalar + % double between 0 and 1 (or between 1 and 100, as a + % percentage). The panel is sized as a fraction of + % the space remaining in its parent after Fixed Size + % panels and inter-panel margins have been subtracted. + % + % c) Stretchable: the specifier is the empty matrix + % []. remaining space in the parent after Fixed and + % Fractional Size panels have been subtracted is + % shared out amongst Stretchable Size panels. + % + % (ii) Absolute Mode - panels hover above their + % parent and do not take up space, as if using the + % position:absolute property in CSS. The packing + % specifier is a 1x4 double vector indicating the + % [left bottom width height] of the panel in + % normalised coordinates of its parent. for example, + % the specifier [0 0 1 1] generates a child panel + % that fills its parent. + % + % SHORTCUTS + % + % ** a small scalar integer, N, (1 to 32) is expanded + % to {[], [], ... []}, with N entries. that is, it + % packs N panels in Relative Mode (Stretchable) and + % shares the available space between them. + % + % ** the call to pack() is recursive, so following a + % packing specifier list, an additional list will + % be used to generate a separate call to pack() on + % each of the children created by the first. hence: + % + % p.pack({[] []}, {[] []}) + % + % will create a 2x2 grid of panels that share the + % space of their parent, "p". since the argument + % "2" expands to {[] []} (see above), the same grid + % can be created using: + % + % p.pack(2, 2) + % + % which is a common idiom in the demos. NB: on + % recursion, the packing dimension is flipped + % automatically, so that a grid is formed. + % + % ** if no arguments are passed at all, a single + % argument {[]} is assumed, so that a single + % additional panel is packed into the parent in + % relative packing mode and with stretchable size. + % + % see also: panel (overview), panel/panel(), select() + % + % LEGACY + % + % the interface to pack() was changed at release + % 2.10 to add support for panels of fixed physical + % size. the interface offered at 2.9 and earlier is + % still available (look inside panel.m - search for + % text "LEGACY" - for details). + + % LEGACY + % + % releases of panel prior to 2.10 did not support + % panels of fixed physical size, and therefore had + % developed a different argument form to that used in + % 2.10 and beyond. specifically, the following + % additional arguments are accepted, for legacy + % support: + % + % 'abs' + % the next argument will be an absolute position, as + % described below. you should avoid using absolute + % positioning mode, in general, since this does not + % take advantage of panel's automatic layout. + % however, on occasion, you may need to take manual + % control of the position of one or more panels. see + % demopanelH for an example. + % + % 1xN row vector (without 'abs') + % pack N new panels along the packing dimension in + % relative mode, with the relative size of each + % given by the elements of the vector. -1 can be + % passed for any elements to mark those panel as + % 'stretchable', so that they fill available space + % left over by other panels packed alongside. the + % sum of the vector (apart from any -1 entries) + % should not come to more than 1, or a warning will + % be generated during laying out. an example would + % be [1/4 1/4 -1], to pack 3 panels, at 25, 25 and + % 50% relative sizes. though, NB, you can use + % percentages instead of fractions if you prefer, in + % which case they should not sum to over 100. so + % that same pack() would be [25 25 -1]. + % + % 1x4 row vector (after 'abs') + % pack 1 new panel using absolute positioning. the + % argument indicates the [left bottom width height] + % of the new panel, in normalised coordinates, as a + % fraction of its parent's position. panels using + % absolute positioning mode are ignored for the sake + % of layout, much like items using + % 'position:absolute' in CSS. + + % handle legacy, parse arguments from varargin into args + args = {}; + while ~isempty(varargin) + + % peel + arg = varargin{1}; + varargin = varargin(2:end); + + % handle shortcut (small integer) on current interface + if isa(arg, 'double') && isscalar(arg) && round(arg) == arg && arg >= 1 && arg <= 32 + arg = cell(1, arg); + end + + % handle current interface - note that the argument + % "recursive" is private and not advertised to the + % user. + if isequal(arg, 'h') || isequal(arg, 'v') || (iscell(arg) && isrow(arg)) || isequal(arg, 'recursive') + args{end + 1} = arg; + continue + end + + % report (DEBUG) + % panel.debugmsg('use of LEGACY interface to pack()', 1); + + % handle legacy case + if isequal(arg, 'abs') + if length(varargin) ~= 1 || ~isnumeric(varargin{1}) || ~isofsize(varargin{1}, [1 4]) + error('panel:LegacyAbsNotFollowedBy1x4', 'the argument "abs" on the legacy interface should be followed by a [1x4] row vector'); + end + abs = varargin{1}; + varargin = varargin(2:end); + args{end + 1} = {abs}; + continue + end + + % handle legacy case + if isa(arg, 'double') && isrow(arg) + arg_ = {}; + for a = 1:length(arg) + aa = arg(a); + if isequal(aa, -1) + arg_{end + 1} = []; else - % pre-R2014b - s = num2str(h); + arg_{end + 1} = aa; end + end + args{end + 1} = arg_; + continue end - - function prop = getPropertyInitialState() - - prop = panel.getPropertyDefaults(); - for key = fieldnames(prop)' - prop.(key{1}) = []; - end - - end - - function value = getPropertyDefault(key) - - persistent defprop - - if isempty(defprop) - defprop = panel.getPropertyDefaults(); - end - - value = defprop.(key); - - end - - function defprop = getPropertyDefaults() - - % root properties - defprop.units = 'mm'; - - % inherited properties - defprop.fontname = get(0, 'defaultAxesFontName'); - defprop.fontsize = get(0, 'defaultAxesFontSize'); - defprop.fontweight = 'normal'; - defprop.margin = {[15 15 5 5] 'mm'}; - - % not inherited properties - % CURRENTLY, NONE! -% defprop.align = false; - - end - - end - - - - - - - - %% ---- STATIC PUBLIC METHODS ---- - - methods (Static = true) - - function p = recover(h_figure) - - % get a handle to the root panel associated with a figure - % - % p = recover(h_fig) - % if you have not got a handle to the root panel of - % the figure h_fig, this call will retrieve it. if - % h_fig is not supplied, gcf is used. - - if nargin < 1 - h_figure = gcf; - end - - p = panel.callbackDispatcher('recover', h_figure); - - end - - function version() - - % report the version of panel that is active - % - % panel.version() - - fid = fopen(which('panel')); - tag = '% Release Version'; - ltag = length(tag); - tagline = 'Unable to determine Release Version'; - while 1 - line = fgetl(fid); - if ~ischar(line) - break - end - if length(line) > ltag && strcmp(line(1:ltag), tag) - tagline = line(3:end); - end - end - fclose(fid); - disp(tagline) - - end - - function panic() - - % call delete on all children of the global workspace, - % to recover from bugs that leave us with uncloseable - % figures. call this as "panel.panic()". - % - % NB: if you have to call panic(), something has gone - % wrong. if you are able to reproduce the problem, - % please contact me to report the bug. - delete(allchild(0)); - - end - - end - - - - - - - %% ---- STATIC PRIVATE METHODS ---- - - methods (Static = true, Access = private) - - function error(id) - - switch id - case 'PanelUncommitted' - throwAsCaller(MException('panel:PanelUncommitted', 'this action cannot be performed on an uncommitted panel')); - case 'InvalidIndexing' - throwAsCaller(MException('panel:InvalidIndexing', 'you cannot index a panel object in this way')); - case 'InternalError' - throwAsCaller(MException('panel:InternalError', 'an internal error occurred')); - otherwise - throwAsCaller(MException('panel:UnknownError', ['an unknown error was generated with id "' id '"'])); - end - - end - - function lockClass() - - persistent hasLocked - if isempty(hasLocked) - - % only lock if not in debug mode - if ~panel.isDebug() - % in production code, must mlock() file at this point, - % to avoid persistent variables being cleared by user - if strcmp(getenv('USERDOMAIN'), 'BERGEN') - % my machine, do nothing - else - mlock - end - end - - % mark that we've handled this - hasLocked = true; - - end - - end - - function debugmsg(msg, focus) - - % focus can be supplied to force only focussed - % messages to be shown - if nargin < 2 - focus = 1; - end - - % display, if in debug mode - if focus - if panel.isDebug() - disp(msg); - end - end - - end - - function state = isDebug() - - % persistent - persistent debug - - % create - if isempty(debug) - try - debug = panel_debug_state(); - catch - debug = false; - end - end - - % ok - state = debug; - - end - - function r = getFractionOfRectangle(r, dim, range) - - switch dim - case 1 - r = [r(1)+range(1)*r(3) r(2) range(2)*r(3) r(4)]; - case 2 - r = [r(1) r(2)+(1-sum(range))*r(4) r(3) range(2)*r(4)]; - otherwise - error('panel:CaseNotCoded', ['case not coded, dim = ' dim ' (internal error)']); - end - - end - - function r = getRectangleOfRectangle(r, s) - - w = r(3); - h = r(4); - r = [r(1)+s(1)*w r(2)+s(2)*h s(3)*w s(4)*h]; - - end - - function a = getUnionRect(a, b) - - if isempty(a) - a = b; - end - if ~isempty(b) - d = a(1) - b(1); - if d > 0 - a(1) = a(1) - d; - a(3) = a(3) + d; - end - d = a(2) - b(2); - if d > 0 - a(2) = a(2) - d; - a(4) = a(4) + d; - end - d = b(1) + b(3) - (a(1) + a(3)); - if d > 0 - a(3) = a(3) + d; - end - d = b(2) + b(4) - (a(2) + a(4)); - if d > 0 - a(4) = a(4) + d; - end - end - - end - - function r = reduceRectangle(r, margin) - - r(1:2) = r(1:2) + margin(1:2); - r(3:4) = r(3:4) - margin(1:2) - margin(3:4); - - end - - function v = normaliseDimension(v, space_size_in_mm) - - v = v ./ [space_size_in_mm space_size_in_mm]; - - end - - function v = resolveUnits(d, units) - - % first, convert into mm - v = d{1}; - switch d{2} - case 'mm' - % ok - case 'cm' - v = v * 10.0; - case 'in' - v = v * 25.4; - case 'pt' - v = v / 72.0 * 25.4; - otherwise - error('panel:CaseNotCoded', ['case not coded, storage units = ' units ' (internal error)']); - end - - % then, convert to specified units - switch units - case 'mm' - % ok - case 'cm' - v = v / 10.0; - case 'in' - v = v / 25.4; - case 'pt' - v = v / 25.4 * 72.0; - otherwise - error('panel:CaseNotCoded', ['case not coded, requested units = ' units ' (internal error)']); - end - - end - - function resizeCallback(obj, evt) - - panel.callbackDispatcher('resize', obj); - - end - - function closeCallback(obj, evt) - - panel.callbackDispatcher('delete', obj); - delete(obj); - - end - - function out = callbackDispatcher(op, data) - - % debug output -% panel.debugmsg(['callbackDispatcher(' op ')...']) - - % persistent store - persistent registeredPanels - - % switch on operation - switch op - - case {'register' 'registerNoClear'} - - % if a root panel is already attached to this - % figure, we could throw an error and refuse to - % create the new object, we could delete the - % existing panel, or we could allow multiple - % panels to be attached to the same figure. - % - % we should allow multiple panels, because they - % may have different parents within the same - % figure (e.g. uipanels). but by default we don't, - % unless the panel.add() static constructor is - % used. - - if strcmp(op, 'register') - - argument_h_figure = data.h_figure; - i = 0; - while i < length(registeredPanels) - i = i + 1; - if registeredPanels(i).h_figure == argument_h_figure - delete(registeredPanels(i)); - i = 0; - end - end - - end - - % register the new panel - if isempty(registeredPanels) - registeredPanels = data; - else - registeredPanels(end+1) = data; - end - - % debug output -% panel.debugmsg(['panel registered (' int2str(length(registeredPanels)) ' now registered)']); - - case 'unregister' - - % debug output -% panel.debugmsg(['on unregister, ' int2str(length(registeredPanels)) ' registered']); - - for r = 1:length(registeredPanels) - if registeredPanels(r) == data - registeredPanels = registeredPanels([1:r-1 r+1:end]); - - % debug output -% panel.debugmsg(['panel unregistered (' int2str(length(registeredPanels)) ' now registered)']); - - return - end - end - - % warn - warning('panel:AbsentOnCallbacksUnregister', 'panel was absent from the callbacks register when it tried to unregister itself'); - - case 'resize' - - argument_h_parent = data; - for r = 1:length(registeredPanels) - if registeredPanels(r).h_parent == argument_h_parent - registeredPanels(r).recomputeLayout([]); - end - end - - case 'recover' - - argument_h_figure = data; - out = []; - for r = 1:length(registeredPanels) - if registeredPanels(r).h_figure == argument_h_figure - if isempty(out) - out = registeredPanels(r); - else - out(end+1) = registeredPanels(r); - end - end - end - - case 'delete' - - argument_h_figure = data; - i = 0; - while i < length(registeredPanels) - i = i + 1; - if registeredPanels(i).h_figure == argument_h_figure - delete(registeredPanels(i)); - i = 0; - end - end - - end - - end - - end - - - - -end + % unrecognised argument + error('panel:InvalidArgument', 'argument to pack() not recognised'); + + end + + % check m_panelType + if p.isObject() + error('panel:PackWhenObjectPanel', ... + 'cannot pack() into this panel - it is already committed as an object panel'); + end + + % if no arguments, simulate an argument of [], to pack + % a single panel of stretchable size + if isempty(args) + args = {{[]}}; + end + + % state + recursive = false; + + % handle arguments one by one + while ~isempty(args) && ischar(args{1}) + + % extract + arg = args{1}; + args = args(2:end); + + % handle string arguments + switch arg + case 'h' + p.packdim = 1; + case 'v' + p.packdim = 2; + case 'recursive' + recursive = true; + otherwise + error('panel:InvalidArgument', ['pack() did not recognise the argument "' arg '"']); + end + end + % if no more arguments that's weird but not bad + if isempty(args) + return + end + % next argument now must be a cell + arg = args{1}; + args = args(2:end); + if ~iscell(arg) + panel.error('InternalError'); + end + % commit as parent + p.commitAsParent(); + % for each element + for i = 1:length(arg) + % get packspec + packspec = arg{i}; + % validate + validate_packspec(packspec); + % handle units + if iscell(packspec) + units = p.getPropertyValue('units'); + packspec{1} = panel.resolveUnits({packspec{1} units}, 'mm'); + end + % create a child + child = panel('pack', p); + child.packspec = packspec; + % store it in the parent + if isempty(p.m_children) + p.m_children = child; + else + p.m_children(end + 1) = child; + end + % recurse (further argumens are passed on) + if ~isempty(args) + child_packdim = flippackdim(p.packdim); + edges = 'hv'; + child.pack('recursive', edges(child_packdim), args{:}); + end + end + + % this must generate a recomputeLayout(), since the + % addition of new panels may affect the layout. any + % recursive call passes 'recursive', so that only the + % root call actually bothers doing a layout. + if ~recursive + p.recomputeLayout([]); + end + + end + + function h_out = select(p, h_object) + + % create or select an axis or object panel + % + % h = p.select(h) + % this call will return the handle of the object + % associated with the panel. if the panel is not yet + % committed, this will involve first committing it + % as an "object panel". if a list of objects ("h") + % is passed, these are the objects associated with + % the panel; if not, a new axis is created by the + % panel when this function is called. + % + % if the object list includes axes, then the "object + % panel" is also known as an "axis panel". in this + % case, the call to select() will make the (first) + % axis current, unless an output argument is + % requested, in which case the handle of the axes + % are returned but no axis is made current. + % + % the passed objects can be user-created axes (e.g. + % a colorbar) or any graphics object that is to have + % its position managed (e.g. a uipanel). your + % mileage may vary with different types of graphics + % object, please let me know. + % + % see also: panel (overview), panel/panel(), pack() + + % handle "all" and "data" + if nargin == 2 && isstring(h_object) && (strcmp(h_object, 'all') || strcmp(h_object, 'data')) + + % collect + h_out = []; + + % commit all uncommitted panels as axis panels by + % selecting them once + if p.isParent() + + % recurse + for c = 1:length(p.m_children) + h_out = [h_out p.m_children(c).select(h_object)]; + end + + elseif p.isUncommitted() + + % select in an axis + h_out = p.select(); + + % plot some data + if strcmp(h_object, 'data') + plot(h_out, randn(100, 1), 'k-'); + end + end + % ok + return + end -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% -% HELPERS -% -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % check m_panelType + if p.isParent() + error('panel:SelectWhenParent', 'cannot select() this panel - it is already committed as a parent panel'); + end -function restore = dashstyle_line(fix, context) + % commit as object + p.commitAsObject(); -% get axis size in mm -h_line = fix.h; -h_axis = get(h_line, 'parent'); -u = get(h_axis, 'units'); -set(h_axis, 'units', 'norm'); -pos = get(h_axis, 'position'); -set(h_axis, 'units', u); -axis_in_mm = pos(3:4) .* context.size_in_mm; - -% recover data -xdata = get(h_line, 'xdata'); -ydata = get(h_line, 'ydata'); -zdata = get(h_line, 'zdata'); -linestyle = get(h_line, 'linestyle'); -marker = get(h_line, 'marker'); - -% empty restore -restore = []; - -% do not handle 3D -if ~isempty(zdata) - warning('panel:NoFixdash3D', 'panel cannot fixdash() a 3D line - no action taken'); - return -end + % assume not a new object + newObject = false; -% get range of axis -ax = axis(h_axis); + % use passed graphics object + if nargin >= 2 -% get scale in each dimension (mm per unit) -sc = axis_in_mm ./ (ax([2 4]) - ax([1 3])); + % validate + if ~all(ishandle(h_object)) + error('panel:InvalidArgument', 'argument to select() must be a list of handles to graphics objects'); + end -% create empty line -data = NaN; + % validate + if ~isempty(p.h_object) + % 02/08/19 I disabled this check because + % I don't see why it's needed (why + % should we not change the managed + % objects on the fly?) + % error('panel:SelectWithObjectWhenObject', 'cannot select() new objects into this panel - it is already managing objects'); + end -% override linestyle -if ~isempty(fix.linestyle) - linestyle = fix.linestyle; -end + % store + p.h_object = h_object; + newObject = true; + + % make sure it has the correct parent - this doesn't + % seem to affect axes, so we do it for all + set(p.h_object, 'parent', p.h_parent); + + end + + % create new axis if necessary + if isempty(p.h_object) + % 'NextPlot', 'replacechildren' + % make sure fonts etc. don't get changed when user + % plots into it + p.h_object = axes( ... + 'Parent', p.h_parent, ... + 'NextPlot', 'replacechildren' ... + ); + newObject = true; + end + + % if wrapped objects include an axis, and no output args, make it current + h_axes = p.getAllManagedAxes(); + if ~isempty(h_axes) && ~nargout + set(p.h_figure, 'CurrentAxes', h_axes(1)); + + % 12/07/11: this call is slow, because it implies "drawnow" + % figure(p.h_figure); + + % 12/07/11: this call is fast, because it doesn't + set(0, 'CurrentFigure', p.h_figure); + + end + + % and return object list + if nargout + h_out = p.h_object; + end + + % this must generate a applyLayout(), since the axis + % will need positioning appropriately + if newObject + % 09/03/12 mitch + % if there isn't a context yet, we'll have to + % recomputeLayout(), in fact, to generate a context first. + % this will happen, for instance, if a single panel + % is generated in a window that was already open + % (no resize event will fire, and since pack() is + % not called, it will not call recomputeLayout() either). + % nonetheless, we have to reposition this object, so + % this forces us to recomputeLayout() now and generate + % that context we need. + if isempty(p.m_context) + p.recomputeLayout([]); + else + p.applyLayout(); + end + end -% transcribe linestyle -linestyle = dashstyle_parse_linestyle(linestyle); -if isempty(linestyle) - return -end + end -% scale -scale = 1; -dashes = linestyle * scale; - -% store for restore -restore.h_line = h_line; -restore.xdata = xdata; -restore.ydata = ydata; - -% create another, separate, line to overlay on the original -% line and render the fixed-up dashes. -restore.h_supp = copyobj(h_line, h_axis); - -% if the original line has markers, we'll have to create yet -% another separate line instance to represent them, because -% they shouldn't be "dashed", as it were. note that we don't -% currently attempt to get the z-order right for these -% new lines. -if ~isequal(marker, 'none') - restore.h_mark = copyobj(h_line, h_axis); - set(restore.h_mark, 'linestyle', 'none'); - set(restore.h_supp, 'marker', 'none'); -else - restore.h_mark = []; -end + end -% hide the original line. this line remains in existence so -% that if there is a legend, it doesn't get messed up. -set(h_line, 'xdata', NaN, 'ydata', NaN); - -% extract pattern length -patlen = sum(dashes); - -% position within pattern is initially zero -pos = 0; - -% linedata -line_xy = complex(xdata, ydata); - -% for each line segment -while length(line_xy) > 1 - - % get line segment - xy = line_xy(1:2); - line_xy = line_xy(2:end); - - % any NaNs, and we're outta here - if any(isnan(xy)) - continue - end - - % get start etc. - O = xy(1); - V = xy(2) - xy(1); - - % get mm length of this line segment - d = sqrt(sum(([real(V) imag(V)] .* sc) .^ 2)); - - % and mm unit vector - U = V / d; - - % generate a long-enough pattern for this segment - n = ceil((pos + d) / patlen); - pat = [0 cumsum(repmat(dashes, [1 n]))] - pos; - pos = d - (pat(end) - patlen); - pat = [pat(1:2:end-1); pat(2:2:end)]; - - % trim spurious segments - pat = pat(:, any(pat >= 0) & any(pat <= d)); - - % skip if that's it - if isempty(pat) - continue - end - - % and reduce ones that are oversized - pat(1) = max(pat(1), 0); - pat(end) = min(pat(end), d); - - % finally, add these segments to the line data - seg = [O + pat * U; NaN(1, size(pat, 2))]; - data = [data seg(:).']; - -end + %% ---- HIDDEN OVERLOADS ---- -% update line -set(restore.h_supp, 'xdata', real(data), 'ydata', imag(data), ... - 'linestyle', '-'); + methods (Hidden = true) -end + function out = vertcat(p, q) + error('panel2:MethodNotImplemented', 'concatenation is not supported by panel (use a cell array instead)'); + end + function out = horzcat(p, q) + error('panel2:MethodNotImplemented', 'concatenation is not supported by panel (use a cell array instead)'); + end -function linestyle = dashstyle_parse_linestyle(linestyle) + function out = cat(dim, p, q) + error('panel2:MethodNotImplemented', 'concatenation is not supported by panel (use a cell array instead)'); + end -if isequal(linestyle, 'none') || isequal(linestyle, '-') - linestyle = []; - return -end + function out = ge(p, q) + error('panel2:MethodNotImplemented', 'inequality operators are not supported by panel'); + end -while 1 - - % if numbers - if isnumeric(linestyle) - if ~isa(linestyle, 'double') || ~isrow(linestyle) || mod(length(linestyle), 2) ~= 0 - break - end - % no need to parse - return - end - - % else, must be char - if ~ischar(linestyle) || ~isrow(linestyle) - break - end - - % translate matlab non-standard codes into codes we can - % easily parse - switch linestyle - case ':' - linestyle = '.'; - case '--' - linestyle = '-'; - end - - % must be only - and . - if any(linestyle ~= '.' & linestyle ~= '-') - break - end - - % transcribe - c = linestyle; - linestyle = []; - for l = c - switch l - case '-' - linestyle = [linestyle 2 0.75]; - case '.' - linestyle = [linestyle 0.5 0.75]; - end - end - return + function out = le(p, q) + error('panel2:MethodNotImplemented', 'inequality operators are not supported by panel'); + end -end + function out = lt(p, q) + error('panel2:MethodNotImplemented', 'inequality operators are not supported by panel'); + end -warning('panel:BadFixdashLinestyle', 'unusable linestyle in fixdash()'); -linestyle = []; + function out = gt(p, q) + error('panel2:MethodNotImplemented', 'inequality operators are not supported by panel'); + end -end + function out = eq(p, q) + out = eq@handle(p, q); + end + function out = ne(p, q) + out = ne@handle(p, q); + end + end -% MISCELLANEOUS + %% ---- PUBLIC HIDDEN GET/SET ---- -function index = isin(list, value) + methods (Hidden = true) -for i = 1:length(list) - if strcmp(value, list{i}) - index = i; - return - end -end + function p = descend(p, indices) -index = 0; + while ~isempty(indices) -end + % validate + if numel(p) > 1 + error('panel:InvalidIndexing', 'you can only use () on a single (scalar) panel'); + end -function dim = flippackdim(dim) + % validate + if ~p.isParent() + error('panel:InvalidIndexing', 'you can only use () on a parent panel'); + end -% this function, used between arguments in a recursive call, -% causes the dim to be switched with each recurse, so that -% we build a grid, rather than a long, long row. -dim = 3 - dim; + % extract + index = indices{1}; + indices = indices(2:end); -end + % only accept numeric + if ~isnumeric(index) || ~isscalar(index) + error('panel:InvalidIndexing', 'you can only use () with scalar indices'); + end + % do the reference + p = p.m_children(index); + + end + + end + + function p_out = subsasgn(p, refs, value) + + % output is always subject + p_out = p; + + % handle () indexing + if strcmp(refs(1).type, '()') + p = p.descend(refs(1).subs); + refs = refs(2:end); + end + + % is that it? + if isempty(refs) + error('panel:InvalidIndexing', 'you cannot assign to a child panel'); + end + + % next ref must be . + if ~strcmp(refs(1).type, '.') + panel.error('InvalidIndexing'); + end + + % either one (.X) or two (.ch.X) + switch numel(refs) + + case 2 + + % validate + if ~strcmp(refs(2).type, '.') + panel.error('InvalidIndexing'); + end + + % validate + switch refs(2).subs + case {'fontname' 'fontsize' 'fontweight'} + case {'margin' 'marginleft' 'marginbottom' 'marginright' 'margintop'} + otherwise + panel.error('InvalidIndexing'); + end + + % avoid computing layout whilst setting descendant + % properties + p.defer(); + + % recurse + switch refs(1).subs + case {'children' 'ch'} + cs = p.m_children; + for c = 1:length(cs) + subsasgn(cs(c), refs(2:end), value); + end + case {'descendants' 'de'} + cs = p.getPanels('*'); + for c = 1:length(cs) + if cs{c} ~= p + subsasgn(cs{c}, refs(2:end), value); + end + end + case {'family' 'fa'} + cs = p.getPanels('*'); + for c = 1:length(cs) + subsasgn(cs{c}, refs(2:end), value); + end + end + + % release for laying out + p.undefer(); + + % mark for appropriate updates + refs(1).subs = refs(2).subs; + + case 1 + + % delegate + p.setPropertyValue(refs(1).subs, value); + + otherwise + panel.error('InvalidIndexing'); + + end + + % update layout as necessary + switch refs(1).subs + case {'fontname' 'fontsize' 'fontweight'} + p.applyLayout('recurse'); + case {'margin' 'marginleft' 'marginbottom' 'marginright' 'margintop'} + p.recomputeLayout([]); + end + + end + + function out = subsref(p, refs) + + % handle () indexing + if strcmp(refs(1).type, '()') + p = p.descend(refs(1).subs); + refs = refs(2:end); + end + + % is that it? + if isempty(refs) + out = p; + return + end + + % next ref must be . + if ~strcmp(refs(1).type, '.') + panel.error('InvalidIndexing'); + end + + % switch on "fieldname" + switch refs(1).subs + + case { ... + 'fontname' 'fontsize' 'fontweight' ... + 'margin' 'marginleft' ... + 'marginbottom' 'marginright' 'margintop' ... + 'units' ... + } + + % delegate this property get + out = p.getPropertyValue(refs(1).subs); + + case 'position' + out = p.getObjectPosition(); + + case 'figure' + out = p.h_figure; + + case 'packspec' + out = p.packspec; + + case 'axis' + if p.isObject() + out = p.getAllManagedAxes(); + else + out = []; + end + + case 'object' + if p.isObject() + h = p.h_object; + ih = ishandle(h); + out = h(ih); + else + out = []; + end + + case {'ch' 'children' 'de' 'descendants' 'fa' 'family'} + + % get the set + switch refs(1).subs + case {'children' 'ch'} + out = {}; + for n = 1:length(p.m_children) + out{n} = p.m_children(n); + end + case {'descendants' 'de'} + out = p.getPanels('*'); + for c = 1:length(out) + if out{c} == p + out = out([1:c - 1 c + 1:end]); + break + end + end + case {'family' 'fa'} + out = p.getPanels('*'); + end + + % we handle a special case of deeper reference + % here, because we are abusing matlab's syntax to + % do it. other cases (non-abusing) will be handled + % recursively, as usual. this is when we go: + % + % p.ch.axis + % + % which isn't syntactically sound since p.ch is a + % cell array (and potentially a non-singular one + % at that). we re-interpret this to mean + % [p.ch{1}.axis p.ch{2}.axis ...], as follows. + if length(refs) > 1 && isequal(refs(2).type, '.') + switch refs(2).subs + case {'axis' 'object'} + pp = out; + out = []; + for i = 1:length(pp) + out = cat(2, out, subsref(pp{i}, refs(2))); + end + refs = refs(2:end); % used up! + otherwise + % give an informative error message + panel.error('InvalidIndexing'); + end + end + case { ... + 'addCallback' 'setCallback' 'clearCallbacks' ... + 'hold' ... + 'refresh' 'export' ... + 'pack' 'repack' ... + 'identify' 'show' ... + } -% STRING PADDING FUNCTIONS + % validate + if length(refs) ~= 2 || ~strcmp(refs(2).type, '()') + error('panel:InvalidIndexing', ['"' refs(1).subs '" is a function (try "help panel/' refs(1).subs '")']); + end -function s = rpad(s, l) + % delegate this function call with no output + builtin('subsref', p, refs); + return -if nargin < 2 - l = 16; -end + case { ... + 'select' 'fixdash' ... + 'xlabel' 'ylabel' 'zlabel' 'title' ... + 'find' ... + } -if length(s) < l - s = [s repmat(' ', 1, l - length(s))]; -end + % validate + if length(refs) ~= 2 || ~strcmp(refs(2).type, '()') + error('panel:InvalidIndexing', ['"' refs(1).subs '" is a function (try "help panel/' refs(1).subs '")']); + end -end + % delegate this function call with output + if nargout + out = builtin('subsref', p, refs); + else + builtin('subsref', p, refs); + end + return -function s = lpad(s, l) + otherwise + panel.error('InvalidIndexing'); -if nargin < 2 - l = 16; -end + end -if length(s) < l - s = [repmat(' ', 1, l - length(s)) s]; -end + % continue + if length(refs) > 1 + out = subsref(out, refs(2:end)); + end -end + end + end + %% ---- UTILITY METHODS ---- -% HANDLE GRAPHICS HELPERS + methods (Access = private) -function h = getParentFigure(h) + function b = ismanagefont(p) -if strcmp(get(h, 'type'), 'figure') - return -else - h = getParentFigure(get(h, 'parent')); -end + % ask root + b = p.m_root.state.manage_font; -end + end -function addHandleCallback(h, name, func) + function b = isdefer(p) -% % get current list of callbacks -% callbacks = get(h, name); -% -% % if empty, turn into a cell -% if isempty(callbacks) -% callbacks = {}; -% elseif iscell(callbacks) -% % only add ourselves once -% for c = 1:length(callbacks) -% if callbacks{c} == func -% return -% end -% end -% else -% callbacks = {callbacks}; -% end -% -% % and add ours (this is friendly, in case someone else has a -% % callback attached) -% callbacks{end+1} = func; -% -% % lay in -% set(h, name, callbacks); + % ask root + b = p.m_root.state.defer ~= 0; -% the above isn't as simple as i thought - for now, we'll -% just stamp on any existing callbacks -set(h, name, func); + end -end + function defer(p) -function store = storeAxisState(h) + % increment + p.m_root.state.defer = p.m_root.state.defer + 1; -% LOCK TICKS AND LIMITS -% -% (LOCK TICKS) -% -% lock state so that the ticks and labels do not change when -% the figure is resized for printing. this is what the user -% will expect, which is why we go through this palaver. -% -% however, for fuck's sake. the following code illustrates -% an idiosyncrasy of matlab (i would call this an -% inconsistency, myself, but there you go). -% -% figure -% axis([0 1 0 1]) -% set(gca, 'ytick', [-1 0 1 2]) -% get(gca, 'yticklabel') -% set(gca, 'yticklabelmode', 'manual') -% -% now, resize the figure window. at least in R2011b, the -% tick labels change on the first resize event. presumably, -% this is because matlab treats the ticklabel value -% differently depending on if the ticklabelmode is auto or -% manual. if it's manual, the value is used as documented, -% and [0 1] is used to label [-1 0 1 2], cyclically. -% however, if the ticklabelmode is auto, and the ticks -% extend outside the figure, then the ticklabels are set -% sensibly, but the _value_ of ticklabel is not consistent -% with what it would need to be to get this tick labelling -% were the mode manual. and, in a final bizarre twist, this -% doesn't become evident until the resize event. i think -% this is a bug, no other way of looking at it; at best it's -% an inconsistency that is either tedious or impossible to -% work around in the general case. -% -% in any case, we have to lock the ticks to manual as we go -% through the print cycle, so that the ticks do not get -% changed if they were in automatic mode. but we mustn't fix -% the tick labels to manual, since if we do we may encounter -% this inconsistency and end up with the wrong tick labels -% in the print out. i can't, at time of writing, think of a -% case where we'd have to fix the tick labels to manual too. -% the possible cases are: -% -% ticks auto, labels auto: in this case, fixing the ticks to -% manual should be enough. -% -% ticks manual, labels auto: leave as is. -% -% ticks manual, labels manual: leave as is. -% -% the only other case is ticks auto, labels manual, which is -% a risky case to use, but in any case we can also fix the -% ticks to manual in that case. thus, our preferred solution -% is to always switch the ticks to manual, if they're not -% already, and otherwise leave things be. -% -% (LOCK LIMITS) -% -% the other thing that may get modified, if the user hasn't -% fixed it, is the axis limits. so we lock them too, any -% that are set to auto, and mark them for unlocking when the -% print is complete. - -store = ''; - -% manual-ise ticks on any axis where they are currently -% automatic, and indicate that we need to switch them back -% afterwards. -if strcmp(get(h, 'XTickMode'), 'auto') - store = [store 'X']; - set(h, 'XTickMode', 'manual'); -end -if strcmp(get(h, 'YTickMode'), 'auto') - store = [store 'Y']; - set(h, 'YTickMode', 'manual'); -end -if strcmp(get(h, 'ZTickMode'), 'auto') - store = [store 'Z']; - set(h, 'ZTickMode', 'manual'); -end + end -% manual-ise limits on any axis where they are currently -% automatic, and indicate that we need to switch them back -% afterwards. -if strcmp(get(h, 'XLimMode'), 'auto') - store = [store 'x']; - set(h, 'XLimMode', 'manual'); -end -if strcmp(get(h, 'YLimMode'), 'auto') - store = [store 'y']; - set(h, 'YLimMode', 'manual'); -end -if strcmp(get(h, 'ZLimMode'), 'auto') - store = [store 'z']; - set(h, 'ZLimMode', 'manual'); -end + function undefer(p) -% % OLD CODE OBSOLETED 25/01/12 - see notes above -% -% % store current state -% store.XTick = get(h, 'XTick'); -% store.XTickMode = get(h, 'XTickMode'); -% store.XTickLabel = get(h, 'XTickLabel'); -% store.XTickLabelMode = get(h, 'XTickLabelMode'); -% store.YTickMode = get(h, 'YTickMode'); -% store.YTick = get(h, 'YTick'); -% store.YTickLabel = get(h, 'YTickLabel'); -% store.YTickLabelMode = get(h, 'YTickLabelMode'); -% store.ZTick = get(h, 'ZTick'); -% store.ZTickMode = get(h, 'ZTickMode'); -% store.ZTickLabel = get(h, 'ZTickLabel'); -% store.ZTickLabelMode = get(h, 'ZTickLabelMode'); -% -% % lock state to manual -% set(h, 'XTickLabelMode', 'manual'); -% set(h, 'XTickMode', 'manual'); -% set(h, 'YTickLabelMode', 'manual'); -% set(h, 'YTickMode', 'manual'); -% set(h, 'ZTickLabelMode', 'manual'); -% set(h, 'ZTickMode', 'manual'); + % decrement + p.m_root.state.defer = p.m_root.state.defer - 1; -end + end -function restoreAxisState(h, store) + function cs = getPanels(p, panelTypes, edgespec, all) -% unmanualise -for item = store - switch item - case {'X' 'Y' 'Z'} - set(h, [item 'TickMode'], 'auto'); - case {'x' 'y' 'z'} - set(h, [upper(item) 'TickMode'], 'auto'); - end -end + % return all the panels that match the specification. + % + % panelTypes "*": return all panels + % panelTypes "s": return all sizeable panels (parent, + % object and uncommitted) + % panelTypes "p": return only physical panels (object + % or uncommitted) + % panelTypes "o": return only object panels + % + % if edgespec/all is specified, only panels matching + % the edgespec are returned (all of them if "all" is + % true, or any of them - the first one, in fact - if + % "all" is false). -% % OLD CODE OBSOLETED 25/01/12 - see notes above -% -% % restore passed state -% set(h, 'XTick', store.XTick); -% set(h, 'XTickMode', store.XTickMode); -% set(h, 'XTickLabel', store.XTickLabel); -% set(h, 'XTickLabelMode', store.XTickLabelMode); -% set(h, 'YTick', store.YTick); -% set(h, 'YTickMode', store.YTickMode); -% set(h, 'YTickLabel', store.YTickLabel); -% set(h, 'YTickLabelMode', store.YTickLabelMode); -% set(h, 'ZTick', store.ZTick); -% set(h, 'ZTickMode', store.ZTickMode); -% set(h, 'ZTickLabel', store.ZTickLabel); -% set(h, 'ZTickLabelMode', store.ZTickLabelMode); + cs = {}; -end + % do not include any that use absolute positioning - + % they stand outside of the sizing model + skip = (numel(p.packspec) == 4) && ~any(panelTypes == '*'); + if p.isParent() + % return if appropriate type + if any(panelTypes == '*s') && ~skip + cs = {p}; + end -% DIM AND EDGE HANDLING + % if edgespec was supplied + if nargin == 4 -% we describe each edge of a panel in terms of "dim" (1 or -% 2, horizontal or vertical) and "edge" (1 or 2, former or -% latter). together, [dim edge] is an "edgespec". + % if we are perpendicular to the specified edge + if p.packdim ~= edgespec(1) -function s = edgestr(edgespec) + if all -s = 'lbrt'; -s = s(edgeindex(edgespec)); + % return all matching + for c = 1:length(p.m_children) + ppp = p.m_children(c).getPanels(panelTypes, edgespec, all); + cs = cat(2, cs, ppp); + end -end + else -function i = edgeindex(edgespec) + % return only the first one + cs = cat(2, cs, p.m_children(1).getPanels(panelTypes, edgespec, all)); -% edge indices. margins are stored as [l b r t] but -% dims are packed left to right and top to bottom, so -% relationship between 'dim' and 'end' and index into -% margin is non-trivial. we call the index into the margin -% the "edgeindex". an "edgespec" is just [dim end], in a -% single array. -i = [1 3; 4 2]; -i = i(edgespec(1), edgespec(2)); + end -end + else + % if we are parallel to the specified edge + if edgespec(2) == 2 + % use last + ppp = p.m_children(end).getPanels(panelTypes, edgespec, all); + cs = cat(2, cs, ppp); -% VARIABLE TYPE HELPERS + else -function val = validate_par(val, argtext, varargin) + % use first + cs = cat(2, cs, p.m_children(1).getPanels(panelTypes, edgespec, all)); -% this helper validates arguments to some functions in the -% main body - -for n = 1:length(varargin) - - % get validation constraint - arg = varargin{n}; - - % handle string list - if iscell(arg) - % string list - if ~isin(arg, val) - error('panel:InvalidArgument', ... - ['invalid argument "' argtext '", "' val '" is not a recognised data value for this option']); - end - continue; - end - - % handle strings - if isstring(arg) - switch arg - case 'empty' - if ~isempty(val) - error('panel:InvalidArgument', ... - ['invalid argument "' argtext '", option does not expect any data']); - end - case 'dimension' - if ~isdimension(val) - error('panel:InvalidArgument', ... - ['invalid argument "' argtext '", option expects a dimension']); - end - case 'scalar' - if ~(isnumeric(val) && isscalar(val) && ~isnan(val)) - error('panel:InvalidArgument', ... - ['invalid argument "' argtext '", option expects a scalar value']); - end - case 'nonneg' - if any(val(:) < 0) - error('panel:InvalidArgument', ... - ['invalid argument "' argtext '", option expects non-negative values only']); - end - case 'integer' - if any(val(:) ~= round(val(:))) - error('panel:InvalidArgument', ... - ['invalid argument "' argtext '", option expects integer values only']); - end - end - continue; - end - - % handle numeric range - if isnumeric(arg) && isofsize(arg, [1 2]) - if any(val(:) < arg(1)) || any(val(:) > arg(2)) - error('panel:InvalidArgument', ... - ['invalid argument "' argtext '", option data must be between ' num2str(arg(1)) ' and ' num2str(arg(2))]); - end - continue; - end - - % not recognised - arg - error('panel:InternalError', 'internal error - bad argument to validate_par (above)'); - -end + end -end + end -function b = checkpar(value, mn, mx) + else -b = isscalar(value) && isnumeric(value) && ~isnan(value); -if b - if nargin >= 2 - b = b && value >= mn; - end - if nargin >= 3 - b = b && value <= mx; - end -end + % else, return all + for c = 1:length(p.m_children) + ppp = p.m_children(c).getPanels(panelTypes); + cs = cat(2, cs, ppp); + end -end + end -function b = isintegral(v) + elseif p.isObject() -b = all(all(v == round(v))); + % return if appropriate type + if any(panelTypes == '*spo') && ~skip + cs = {p}; + end -end + else -function b = isstring(value) + % return if appropriate type + if any(panelTypes == '*sp') && ~skip + cs = {p}; + end -sz = size(value); -b = ischar(value) && length(sz) == 2 && sz(1) == 1 && sz(2) >= 1; + end -end + end -function b = isdimension(value) + function commitAsParent(p) -b = isa(value, 'double') && (isscalar(value) || isofsize(value, [1 4])); + if p.isUncommitted() + p.m_panelType = p.PANEL_TYPE_PARENT; + elseif p.isObject() + error('panel:AlreadyCommitted', 'cannot make this panel a parent panel, it is already an object panel'); + end -end + end -function b = isscalardimension(value) + function commitAsObject(p) -b = isa(value, 'double') && isscalar(value); + if p.isUncommitted() + p.m_panelType = p.PANEL_TYPE_OBJECT; + elseif p.isParent() + error('panel:AlreadyCommitted', 'cannot make this panel an object panel, it is already a parent panel'); + end -end + end -function b = isofsize(value, siz) + function b = isRoot(p) -sz = size(value); -b = length(sz) == length(siz) && all(sz == siz); + b = isempty(p.parent); -end + end -function b = isaxis(h) + function b = isParent(p) -b = ishandle(h) && strcmp(get(h, 'type'), 'axes'); + b = p.m_panelType == p.PANEL_TYPE_PARENT; -end + end -function validate_packspec(packspec) + function b = isObject(p) - % stretchable - if isempty(packspec) - return - end + b = p.m_panelType == p.PANEL_TYPE_OBJECT; - % scalar - if isa(packspec, 'double') && isscalar(packspec) + end - % fraction - if packspec > 0 && packspec <= 1 - return - end + function b = isUncommitted(p) - % percentage - if packspec > 1 && packspec <= 100 - return - end + b = p.m_panelType == p.PANEL_TYPE_UNCOMMITTED; - end + end - % fixed - if iscell(packspec) && isscalar(packspec) + function h_axes = getAllManagedAxes(p) - % delve - d = packspec{1}; - if isa(d, 'double') && isscalar(d) && d > 0 - return - end + h_axes = []; + for n = 1:length(p.h_object) + h = p.h_object(n); + if isaxis(h) + h_axes = [h_axes h]; + end + end - end + end - % abs - if isa(packspec, 'double') && isofsize(packspec, [1 4]) && all(packspec(3:4)>0) - return - end + function h_object = getOrCreateAxis(p) - % otherwise, bad form - error('panel:BadPackingSpecifier', 'the packing specifier was not valid - see help panel/pack'); + switch p.m_panelType -end + case p.PANEL_TYPE_PARENT + + % create if not present + if isempty(p.h_object) + + % 'Visible', 'off' + % this is the hidden axis of a parent panel, + % used for displaying a parent panel's xlabel, + % ylabel and title, but not as a plotting axis + % + % 'NextPlot', 'replacechildren' + % make sure fonts etc. don't get changed when user + % plots into it + p.h_object = axes( ... + 'Parent', p.h_parent, ... + 'Visible', 'off', ... + 'NextPlot', 'replacechildren' ... + ); + + % make sure it's unitary, to help us in + % positioning labels and title + axis(p.h_object, [0 1 0 1]); + + % refresh this axis position + p.applyLayout(); + + end + + % ok + h_object = p.h_object; + + case p.PANEL_TYPE_OBJECT + % ok + h_object = p.getAllManagedAxes(); + if isempty(h_object) + error('panel:ManagedObjectNotAnAxis', 'this object panel does not manage an axis'); + end + case p.PANEL_TYPE_UNCOMMITTED + panel.error('PanelUncommitted'); + + end + + end + + function removeChild(p, child) + + % if not a parent, fail but warn (shouldn't happen) + if ~p.isParent() + warning('panel:NotParentOnRemoveChild', 'i am not a parent (in removeChild())'); + return + end + + % remove from children + for c = 1:length(p.m_children) + if p.m_children(c) == child + p.m_children = p.m_children([1:c - 1 c + 1:end]); + return + end + end + + % warn + warning('panel:ChildAbsentOnRemoveChild', 'child not found (in removeChild())'); + + end + + function h = getShowAxis(p) + + if p.isRoot() + if isempty(p.h_showAxis) + + % create + p.h_showAxis = axes( ... + 'Parent', p.h_parent, ... + 'units', 'normalized', ... + 'position', [0 0 1 1] ... + ); + + % move to bottom + c = get(p.h_parent, 'children'); + c = [c(2:end); c(1)]; + set(p.h_parent, 'children', c); + + % finalise axis + set(p.h_showAxis, ... + 'xtick', [], 'ytick', [], ... + 'color', 'none', 'box', 'off' ... + ); + axis(p.h_showAxis, [0 1 0 1]); + + % hold + hold(p.h_showAxis, 'on'); + + end + + % return it + h = p.h_showAxis; + + else + h = p.parent.getShowAxis(); + end + + end + + function fireCallbacks(p, event) + + % for each attached callback + for c = 1:length(p.m_callback) + + % extract + callback = p.m_callback{c}; + func = callback{1}; + userdata = callback{2}; + + % fire + data = []; + data.panel = p; + data.event = event; + data.context = p.m_context; + data.userdata = userdata; + func(data); + + end + + end + + end + + %% ---- LAYOUT METHODS ---- + + methods + + function refresh(p) + + % recompute layout of all panels + % + % p.refresh() + % recompute the layout of all panels from scratch. + % this should not usually be required, and is + % provided primarily for legacy support. + + % LEGACY + % + % NB: if you pass 'defer' to the constructor, calling + % refresh() both recomputes the layout and releases + % the defer mode. future changes to properties (e.g. + % margins) will cause immediate recomputation of the + % layout, so only call refresh() when you're done. + + % bubble up to root + if ~p.isRoot() + p.m_root.refresh(); + return + end + + % release defer + p.state.defer = 0; + + % debug output + % panel.debugmsg(['refresh "' p.state.name '"...']); + + % call recomputeLayout + p.recomputeLayout([]); + + end + + end + + methods (Access = private) + + function do_fixdash(p, context) + + % if context is [], this is _after_ the layout for + % export, so we need to restore + if isempty(context) + + % restore lines we changed to their original state + for r = 1:length(p.m_fixdash_restore) + + % get + restore = p.m_fixdash_restore{r}; + + % if empty, no change was made + if ~isempty(restore) + set(restore.h_line, ... + 'xdata', restore.xdata, 'ydata', restore.ydata); + delete([restore.h_supp restore.h_mark]); + end + + end + + else + + % % get handles to objects that still exist + % h_lines = p.m_fixdash(ishandle(p.m_fixdash)); + + % no restores + p.m_fixdash_restore = {}; + + % for each line + for i = 1:length(p.m_fixdash) + + % get + fix = p.m_fixdash{i}; + + % final check + if ~ishandle(fix.h) || ~isequal(get(fix.h, 'type'), 'line') + continue + end + + % apply dashstyle + p.m_fixdash_restore{end + 1} = dashstyle_line(fix, context); + + end + + end + + end + + function p = recomputeLayout(p, context) + + % this function recomputes the layout from scratch. + % this means calculating the sizes of the root panel + % and all descendant panels. after this is completed, + % the function calls applyLayout to effect the new + % layout. + + % if not root, bubble up to root + if ~p.isRoot() + p.m_root.recomputeLayout(context); + return + end + + % if in defer mode, do not compute layout + if p.isdefer() + return + end + + % if no context supplied (e.g. on resize events), use + % the figure window (a context is supplied if + % exporting to an image file). + if isempty(context) + context.mode = panel.LAYOUT_MODE_NORMAL; + context.size_in_mm = []; + context.rect = [0 0 1 1]; + end + + % debug output + % panel.debugmsg(['recomputeLayout "' p.state.name '"...']); + + % % root may have a packspec of its own + % if ~isempty(p.packspec) + % if isscalar(p.packspec) + % % this should never happen, because it should be + % % caught when the packspec is set in repack() + % warning('panel:RootPanelCannotUseRelativeMode', 'the root panel uses relative positioning mode - this is ignored'); + % else + % context.rect = p.packspec; + % end + % end + + % if not given a context size, use the size on screen + % of the parent figure + if isempty(context.size_in_mm) + + % get context (whole parent) size in its units + pp = get(p.h_figure, 'position'); + context_size = pp(3:4); + + % defaults, in case this fails for any reason + screen_size = [1280 1024]; + if ismac + screen_dpi = 72; + else + screen_dpi = 96; + end + + % get screen DPI + try + local_screen_dpi = get(0, 'ScreenPixelsPerInch'); + if ~isempty(local_screen_dpi) + screen_dpi = local_screen_dpi; + end + end + + % get screen size + try + local_screen_size = get(0, 'ScreenSize'); + if ~isempty(local_screen_size) + screen_size = local_screen_size; + end + end + + % get figure width and height on screen + switch get(p.h_figure, 'Units') + + case 'points' + points_per_inch = 72; + context.size_in_mm = context_size / points_per_inch * 25.4; + + case 'inches' + context.size_in_mm = context_size * 25.4; + + case 'centimeters' + context.size_in_mm = context_size * 10.0; + + case 'pixels' + context.size_in_mm = context_size / screen_dpi * 25.4; + + case 'characters' + context_size = context_size .* [5 13]; % convert to pixels (based on empirical measurement) + context.size_in_mm = context_size / screen_dpi * 25.4; + + case 'normalized' + context_size = context_size .* screen_size(3:4); % convert to pixels (based on screen size) + context.size_in_mm = context_size / screen_dpi * 25.4; + + otherwise + error('panel:CaseNotCoded', ['case not coded, (Parent Units are ' get(p.h_figure, 'Units') ')']); + + end + + end + + % that's the figure size, now we need the size of our + % parent, if it's not the figure too + if p.h_parent ~= p.h_figure + units = get(p.h_parent, 'units'); + set(p.h_parent, 'units', 'normalized'); + pos = get(p.h_parent, 'position'); + set(p.h_parent, 'units', units); + context.size_in_mm = context.size_in_mm .* pos(3:4); + end + + % for the root, we apply the margins here, since it's + % a special case because there's always exactly one of + % it + margin = p.getPropertyValue('margin', 'mm'); + m = margin([1 3]) / context.size_in_mm(1); + context.rect = context.rect + [m(1) 0 -sum(m) 0]; + m = margin([2 4]) / context.size_in_mm(2); + context.rect = context.rect + [0 m(1) 0 -sum(m)]; + + % now, recurse + p.recurseComputeLayout(context); + + % clear h_showAxis when we recompute the layout + if ~isempty(p.h_showAxis) + delete(p.h_showAxis); + p.h_showAxis = []; + end + + % having computed the layout, we now apply it, + % starting at the root panel. + p.applyLayout('recurse'); + + end + + function recurseComputeLayout(p, context) + + % store context + p.m_context = context; + + % if no children, do nothing further + if isempty(p.m_children) + return + end + + % else, we're going to recompute the layout for our + % children + margins = []; + + % get size to pack into + mm_canvas = context.size_in_mm(p.packdim); + mm_context = mm_canvas * context.rect(2 + p.packdim); + + % get list of children that are packed relative - we + % do this because the computation only handles these + % relative children; absolute packed children are + % ignored through the computation, and are just packed + % as specified when the time comes. + rel_list = []; + + % for each child + for i = 1:length(p.m_children) + + % get child + c = p.m_children(i); + + % is it packed abs? + if isofsize(c.packspec, [1 4]) + continue + end + + % if not, it's packed relative, so add to list + rel_list(end + 1) = i; + + end + + % array of actual sizes as fraction of parent (note we + % only represent the rel_list). + zz = zeros(1, length(rel_list)); + sz_phys = zz; + sz_frac = zz; + i_stretch = zz; + + % for each child that is packed relative + for i = 1:length(rel_list) + + % get child + c = p.m_children(rel_list(i)); + + % get internal margin + margin = c.getPropertyValue('margin', 'mm'); + if p.packdim == 2 + margin = margin([2 4]); + margin = fliplr(margin); % doclink FLIP_PACKDIM_2 - same reason, here! + else + margin = margin([1 3]); + end + margins(i:i + 1, i) = margin'; + + % subtract fixed size packspec from packing size + if iscell(c.packspec) + % NB: fixed size is always _stored_ in mm! + sz_phys(i) = c.packspec{1}; + end + + % get relative packing sizes + if isnumeric(c.packspec) && isscalar(c.packspec) + % NB: relative size is a scalar numeric + sz_frac(i) = c.packspec; + % convert perc to frac + if sz_frac(i) > 1 + sz_frac(i) = sz_frac(i) / 100; + end + end + + % get stretch packing size + if isempty(c.packspec) + % NB: these will be filled later + i_stretch(i) = 1; + end + + % else, it's an abs packing size, and we can ignore + % it for this phase of layout + + end + + % finalise internal margins (that is, the margin at + % each boundary between two adjacent relative packed + % panels is the maximum of the margins specified by + % each of the pair). + margins = max(margins, [], 2); + margins = margins(2:end - 1)'; + + % subtract internal margins to give available space + % for objects (in mm) + mm_objects = mm_context - sum(margins); + + % now, subtract physically sized objects to give + % available space to share out amongst panels that + % specify their size as a fraction. + mm_share = mm_objects - sum(sz_phys); + + % and now stretch items can be given their actual + % fractional size, since we now know who they are + % sharing space with. + sz_frac(find(i_stretch)) = (1 - sum(sz_frac)) / sum(i_stretch); + + % and we can now get the real physical size of all the + % fractionally-sized panels in mm. + sz_frac = sz_frac * mm_share; + + % finally, we've got the physical boundaries of + % everything; let's just tidy that up. + sz = [[sz_phys + sz_frac]; margins 0]; + sz = sz(1:end - 1); + + % and let's normalise the physical boundaries, because + % we're actually going to specify them to matlab in + % normalised form, even though we computed them in mm. + if ~isempty(sz) + + % do it + sz_norm = reshape([0 cumsum(sz / mm_context)]', 2, [])'; + + % for packdim 2, we pack from the top, whereas + % matlab's position property packs from the bottom, so + % we have to flip these. doclink FLIP_PACKDIM_2. + if p.packdim == 2 + sz_norm = fliplr(1 - sz_norm); + end + + end + + % recurse + for i = 1:length(p.m_children) + + % get child + c = p.m_children(i); + + % handle abs packed panels + if isofsize(c.packspec, [1 4]) + + % child context + child_context = context; + rect = child_context.rect; + rect([1 3]) = c.packspec([1 3]) * rect(3) + [rect(1) 0]; + rect([2 4]) = c.packspec([2 4]) * rect(4) + [rect(2) 0]; + child_context.rect = rect; + + else + + % child context + child_context = context; + rr = sz_norm(1, :); + sz_norm = sz_norm(2:end, :); % sz_norm has only as many entries as there are rel-packed panels + ri = p.packdim + [0 2]; + a = child_context.rect(ri(1)); + b = child_context.rect(ri(2)); + child_context.rect(ri) = [a + rr(1) * b diff(rr) * b]; + + end + + % recurse + c.recurseComputeLayout(child_context); + + end + + end + + function applyLayout(p, varargin) + + % this function applies the layout that is stored in + % each panel objects "m_context" member, and fixes up + % the position of any associated objects (such as axis + % group labels). + + % skip if disabled + if p.isdefer() + return + end + + % debug output + % panel.debugmsg(['applyLayout "' p.state.name '"...']); + + % defaults + recurse = false; + + % handle arguments + while ~isempty(varargin) + + % get + arg = varargin{1}; + varargin = varargin(2:end); + + % handle + switch arg + case 'recurse' + recurse = true; + otherwise + panel.error('InternalError'); + end + + end + + % recurse + if recurse + pp = p.getPanels('*'); + else + pp = {p}; + end + + % why do we have to split the applyLayout() operation + % into two? + % + % because the "group labels" are positioned with + % respect to the axes in their group depending on + % whether those axes have tick labels, and what those + % tick labels are. if those tick labels are in + % automatic mode (as they usually are), they may + % change when those axes are positioned. since an axis + % group may contain many of these nested deep, we have + % to position all axes (step 1) first, then (step 2) + % position any group labels. + + % step 1 + for pi = 1:length(pp) + pp{pi}.applyLayout1(); + end + + % step 2 + for pi = 1:length(pp) + pp{pi}.applyLayout2(); + end + + % callbacks + for pi = 1:length(pp) + fireCallbacks(pp{pi}, 'layout-updated'); + end + + end + + function r = getObjectPosition(p) + + % get packed position + r = p.m_context.rect; + + % if empty, must be absolute position + if isempty(r) + r = p.packspec; + pp = getObjectPosition(p.parent); + r = panel.getRectangleOfRectangle(pp, r); + end + + end + + function applyLayout1(p) + + % if no context yet, skip this call + if isempty(p.m_context) + return + end + + % if no managed objects, skip this call + if isempty(p.h_object) + return + end + + % debug output + % panel.debugmsg(['applyLayout1 "' p.state.name '"...']); + + % handle LAYOUT_MODE + switch p.m_context.mode + + case panel.LAYOUT_MODE_PREPRINT + + % if in LAYOUT_MODE_PREPRINT, store current axis + % layout (ticks and ticklabels) and lock them into + % manual mode so they don't get changed during the + % print operation + h_axes = p.getAllManagedAxes(); + for n = 1:length(h_axes) + p.state.store{n} = storeAxisState(h_axes(n)); + end + + case panel.LAYOUT_MODE_POSTPRINT + + % if in LAYOUT_MODE_POSTPRINT, restore axis + % layout, leaving it as it was before we ran + % export + h_axes = p.getAllManagedAxes(); + for n = 1:length(h_axes) + restoreAxisState(h_axes(n), p.state.store{n}); + end + + end + + % position it + try + set(p.h_object, 'position', p.getObjectPosition(), 'units', 'normalized'); + catch err + if strcmp(err.identifier, 'MATLAB:hg:set_chck:DimensionsOutsideRange') + w = warning('query', 'backtrace'); + warning off backtrace; + warning('panel:PanelZeroSize', 'a panel had zero size, and the managed object was hidden'); + set(p.h_object, 'position', [-0.3 -0.3 0.2 0.2]); + if strcmp(w.state, 'on') + warning on backtrace; + end + elseif strcmp(err.identifier, 'MATLAB:class:InvalidHandle') + % this will happen if the user deletes the managed + % objects manually. an obvious way that this + % happens is if the user select()s some panels so + % that axes get created, then calls clf. it would + % be nice if we could clear the panels attached to + % a figure in response to a clf call, but there + % doesn't seem any obvious way to pick up the clf + % call, only the delete(objects) that follows, and + % this is indistinguishable from a call by the + % user to delete(my_axis), for instance. how are + % we to respond if the user deletes the axis the + % panel is managing? it's not clear. so, we'll + % just fail silently, for now, and these panels + % will either never be used again (and will be + % destroyed when the figure is closed) or will be + % destroyed when the user creates a new panel on + % this figure. either way, i think, no real harm + % done. + % w = warning('query', 'backtrace'); + % warning off backtrace + % warning('panel:PanelObjectDestroyed', 'the object managed by a panel has been destroyed'); + % if strcmp(w.state, 'on') + % warning on backtrace + % end + % panel.debugmsg('***WARNING*** the object managed by a panel has been destroyed'); + return + else + rethrow(err); + end + end + + % if managing fonts + if p.ismanagefont() + + % apply properties to objects + h = p.h_object; + + % get those which are axes + h_axes = p.getAllManagedAxes(); + + % and labels/title objects, for any that are axes + for n = 1:length(h_axes) + h = [h ... + get(h_axes(n), 'xlabel') ... + get(h_axes(n), 'ylabel') ... + get(h_axes(n), 'zlabel') ... + get(h_axes(n), 'title') ... + ]; + end + + % apply font properties + set(h, ... + 'fontname', p.getPropertyValue('fontname'), ... + 'fontsize', p.getPropertyValue('fontsize'), ... + 'fontweight', p.getPropertyValue('fontweight') ... + ); + + end + + end + + function applyLayout2(p) + + % if no context yet, skip this call + if isempty(p.m_context) + return + end + + % if no object, skip this call + if isempty(p.h_object) + return + end + + % if not a parent, skip this call + if ~p.isParent() + return + end + + % if not an axis, skip this call - NB: this is not a + % displayed and managed object, rather it is the + % invisible axis used to display parent labels/titles. + % we checked above if this panel is a parent. thus, + % the member h_object must be scalar, if it is + % non-empty. + if ~isaxis(p.h_object) + return + end + + % debug output + % panel.debugmsg(['applyLayout2 "' p.state.name '"...']); + + % matlab moves x/ylabels around depending on + % whether the axis in question has any x/yticks, + % so that the label is always "near" the axis. + % we try to do the same, but it's hack-o-rama. + + % calibration offsets - i measured these + % empirically, what a load of shit + font_fudge = [2 1 / 3]; + nofont_fudge = [2 0]; + + % do xlabel + cs = p.getPanels('o', [2 2], true); + y = 0; + for c = 1:length(cs) + ch = cs{c}; + h_axes = ch.getAllManagedAxes(); + for h_axis = h_axes + % only if there are some tick labels, and they're + % at the bottom... + if ~isempty(get(h_axis, 'xticklabel')) && ~isempty(get(h_axis, 'xtick')) ... + && strcmp(get(h_axis, 'xaxislocation'), 'bottom') + fontoffset_mm = get(h_axis, 'fontsize') * font_fudge(2) + font_fudge(1); + y = max(y, fontoffset_mm); + end + end + end + y = max(y, get(p.h_object, 'fontsize') * nofont_fudge(2) + nofont_fudge(1)); + + % convert and lay in + axisheight_mm = p.m_context.size_in_mm(2) * p.m_context.rect(4); + y = y / axisheight_mm; + set(get(p.h_object, 'xlabel'), ... + 'VerticalAlignment', 'Cap', ... + 'Units', 'Normalized', ... + 'Position', [0.5 -y 1]); + + % calibration offsets - i measured these + % empirically, what a load of shit + font_fudge = [3 1 / 6]; + nofont_fudge = [2 0]; + + % do ylabel + cs = p.getPanels('o', [1 1], true); + x = 0; + for c = 1:length(cs) + ch = cs{c}; + h_axes = ch.getAllManagedAxes(); + for h_axis = h_axes + % only if there are some tick labels, and they're + % at the left... + if ~isempty(get(h_axis, 'yticklabel')) && ~isempty(get(h_axis, 'ytick')) ... + && strcmp(get(h_axis, 'yaxislocation'), 'left') + yt = get(h_axis, 'yticklabel'); + if ischar(yt) + ml = size(yt, 2); + else + ml = 0; + for i = 1:length(yt) + ml = max(ml, length(yt{i})); + end + end + fontoffset_mm = get(h_axis, 'fontsize') * ml * font_fudge(2) + font_fudge(1); + x = max(x, fontoffset_mm); + end + end + end + x = max(x, get(p.h_object, 'fontsize') * nofont_fudge(2) + nofont_fudge(1)); + + % convert and lay in + axisheight_mm = p.m_context.size_in_mm(1) * p.m_context.rect(3); + x = x / axisheight_mm; + set(get(p.h_object, 'ylabel'), ... + 'VerticalAlignment', 'Bottom', ... + 'Units', 'Normalized', ... + 'Position', [-x 0.5 1]); + + % calibration offsets - made up based on the + % ones i measured for the labels + nofont_fudge = [2 0]; + + % get y position + y = max(y, get(p.h_object, 'fontsize') * nofont_fudge(2) + nofont_fudge(1)); + + % convert and lay in + axisheight_mm = p.m_context.size_in_mm(2) * p.m_context.rect(4); + y = y / axisheight_mm; + set(get(p.h_object, 'title'), ... + 'VerticalAlignment', 'Bottom', ... + 'Position', [0.5 1 + y 1]); + + % 21/11/19 move to bottom of z-index stack so + % that it does not interfere with mouse + % interactions with the other axes (e.g. + % zooming) + uistack(p.h_object, 'bottom'); + + end + + end + + %% ---- PROPERTY METHODS ---- + + methods (Access = private) + + function value = getPropertyValue(p, key, units) + + value = p.prop.(key); + + if isempty(value) + + % inherit + if ~isempty(p.parent) + switch key + case {'fontname' 'fontsize' 'fontweight' 'margin' 'units'} + if nargin == 3 + value = p.parent.getPropertyValue(key, units); + else + value = p.parent.getPropertyValue(key); + end + return + end + end + + % default + if isempty(value) + value = panel.getPropertyDefault(key); + end + + end + + % translate dimensions + switch key + case {'margin'} + if nargin < 3 + units = p.getPropertyValue('units'); + end + value = panel.resolveUnits(value, units); + end + + end + + function setPropertyValue(p, key, value) + + % root properties + switch key + case 'units' + if ~isempty(p.parent) + p.parent.setPropertyValue(key, value); + return + end + end + + % value validation + switch key + case 'units' + invalid = ~((isstring(value) && isin({'mm' 'in' 'cm' 'pt'}, value)) || isempty(value)); + case 'fontname' + invalid = ~(isstring(value) || isempty(value)); + case 'fontsize' + invalid = ~((isnumeric(value) && isscalar(value) && value >= 4 && value <= 60) || isempty(value)); + case 'fontweight' + invalid = ~((isstring(value) && isin({'normal' 'bold'}, value)) || isempty(value)); + case 'margin' + invalid = ~((isdimension(value)) || isempty(value)); + case {'marginleft' 'marginbottom' 'marginright' 'margintop'} + invalid = ~isscalardimension(value); + otherwise + error('panel:UnrecognisedProperty', ['unrecognised property "' key '"']); + end + + % value validation + if invalid + error('panel:InvalidValueForProperty', ['invalid value for property "' key '"']); + end + + % marginX properties + switch key + case {'marginleft' 'marginbottom' 'marginright' 'margintop'} + index = isin({'left' 'bottom' 'right' 'top'}, key(7:end)); + element = value; + value = p.getPropertyValue('margin'); + value(index) = element; + key = 'margin'; + end + + % translate dimensions + switch key + case {'margin'} + if isscalar(value) + value = value * [1 1 1 1]; + end + if ~isempty(value) + units = p.getPropertyValue('units'); + value = {panel.resolveUnits({value units}, 'mm') 'mm'}; + end + end + + % lay in + p.prop.(key) = value; + + end + + end + + methods (Static = true, Access = private) + + function s = fignum(h) + + % handled differently pre/post 2014b + if isa(h, 'matlab.ui.Figure') + % R2014b + s = num2str(h.Number); + else + % pre-R2014b + s = num2str(h); + end + end + + function prop = getPropertyInitialState() + + prop = panel.getPropertyDefaults(); + for key = fieldnames(prop)' + prop.(key{1}) = []; + end + + end + + function value = getPropertyDefault(key) + + persistent defprop + + if isempty(defprop) + defprop = panel.getPropertyDefaults(); + end + + value = defprop.(key); + + end + + function defprop = getPropertyDefaults() + + % root properties + defprop.units = 'mm'; + + % inherited properties + defprop.fontname = get(0, 'defaultAxesFontName'); + defprop.fontsize = get(0, 'defaultAxesFontSize'); + defprop.fontweight = 'normal'; + defprop.margin = {[15 15 5 5] 'mm'}; + + % not inherited properties + % CURRENTLY, NONE! + % defprop.align = false; + + end + + end + + %% ---- STATIC PUBLIC METHODS ---- + + methods (Static = true) + + function p = recover(h_figure) + + % get a handle to the root panel associated with a figure + % + % p = recover(h_fig) + % if you have not got a handle to the root panel of + % the figure h_fig, this call will retrieve it. if + % h_fig is not supplied, gcf is used. + + if nargin < 1 + h_figure = gcf; + end + + p = panel.callbackDispatcher('recover', h_figure); + + end + + function version() + + % report the version of panel that is active + % + % panel.version() + + fid = fopen(which('panel')); + tag = '% Release Version'; + ltag = length(tag); + tagline = 'Unable to determine Release Version'; + while 1 + line = fgetl(fid); + if ~ischar(line) + break + end + if length(line) > ltag && strcmp(line(1:ltag), tag) + tagline = line(3:end); + end + end + fclose(fid); + disp(tagline); + + end + + function panic() + + % call delete on all children of the global workspace, + % to recover from bugs that leave us with uncloseable + % figures. call this as "panel.panic()". + % + % NB: if you have to call panic(), something has gone + % wrong. if you are able to reproduce the problem, + % please contact me to report the bug. + delete(allchild(0)); + + end + + end + + %% ---- STATIC PRIVATE METHODS ---- + + methods (Static = true, Access = private) + + function error(id) + + switch id + case 'PanelUncommitted' + throwAsCaller(MException('panel:PanelUncommitted', 'this action cannot be performed on an uncommitted panel')); + case 'InvalidIndexing' + throwAsCaller(MException('panel:InvalidIndexing', 'you cannot index a panel object in this way')); + case 'InternalError' + throwAsCaller(MException('panel:InternalError', 'an internal error occurred')); + otherwise + throwAsCaller(MException('panel:UnknownError', ['an unknown error was generated with id "' id '"'])); + end + + end + + function lockClass() + + persistent hasLocked + if isempty(hasLocked) + + % only lock if not in debug mode + if ~panel.isDebug() + % in production code, must mlock() file at this point, + % to avoid persistent variables being cleared by user + if strcmp(getenv('USERDOMAIN'), 'BERGEN') + % my machine, do nothing + else + mlock; + end + end + + % mark that we've handled this + hasLocked = true; + + end + + end + + function debugmsg(msg, focus) + + % focus can be supplied to force only focussed + % messages to be shown + if nargin < 2 + focus = 1; + end + + % display, if in debug mode + if focus + if panel.isDebug() + disp(msg); + end + end + + end + + function state = isDebug() + + % persistent + persistent debug + + % create + if isempty(debug) + try + debug = panel_debug_state(); + catch + debug = false; + end + end + + % ok + state = debug; + + end + + function r = getFractionOfRectangle(r, dim, range) + + switch dim + case 1 + r = [r(1) + range(1) * r(3) r(2) range(2) * r(3) r(4)]; + case 2 + r = [r(1) r(2) + (1 - sum(range)) * r(4) r(3) range(2) * r(4)]; + otherwise + error('panel:CaseNotCoded', ['case not coded, dim = ' dim ' (internal error)']); + end + + end + + function r = getRectangleOfRectangle(r, s) + + w = r(3); + h = r(4); + r = [r(1) + s(1) * w r(2) + s(2) * h s(3) * w s(4) * h]; + + end + + function a = getUnionRect(a, b) + + if isempty(a) + a = b; + end + if ~isempty(b) + d = a(1) - b(1); + if d > 0 + a(1) = a(1) - d; + a(3) = a(3) + d; + end + d = a(2) - b(2); + if d > 0 + a(2) = a(2) - d; + a(4) = a(4) + d; + end + d = b(1) + b(3) - (a(1) + a(3)); + if d > 0 + a(3) = a(3) + d; + end + d = b(2) + b(4) - (a(2) + a(4)); + if d > 0 + a(4) = a(4) + d; + end + end + + end + + function r = reduceRectangle(r, margin) + + r(1:2) = r(1:2) + margin(1:2); + r(3:4) = r(3:4) - margin(1:2) - margin(3:4); + + end + + function v = normaliseDimension(v, space_size_in_mm) + + v = v ./ [space_size_in_mm space_size_in_mm]; + + end + + function v = resolveUnits(d, units) + + % first, convert into mm + v = d{1}; + switch d{2} + case 'mm' + % ok + case 'cm' + v = v * 10.0; + case 'in' + v = v * 25.4; + case 'pt' + v = v / 72.0 * 25.4; + otherwise + error('panel:CaseNotCoded', ['case not coded, storage units = ' units ' (internal error)']); + end + + % then, convert to specified units + switch units + case 'mm' + % ok + case 'cm' + v = v / 10.0; + case 'in' + v = v / 25.4; + case 'pt' + v = v / 25.4 * 72.0; + otherwise + error('panel:CaseNotCoded', ['case not coded, requested units = ' units ' (internal error)']); + end + + end + + function resizeCallback(obj, evt) + + panel.callbackDispatcher('resize', obj); + + end + + function closeCallback(obj, evt) + + panel.callbackDispatcher('delete', obj); + delete(obj); + + end + + function out = callbackDispatcher(op, data) + + % debug output + % panel.debugmsg(['callbackDispatcher(' op ')...']) + + % persistent store + persistent registeredPanels + + % switch on operation + switch op + + case {'register' 'registerNoClear'} + + % if a root panel is already attached to this + % figure, we could throw an error and refuse to + % create the new object, we could delete the + % existing panel, or we could allow multiple + % panels to be attached to the same figure. + % + % we should allow multiple panels, because they + % may have different parents within the same + % figure (e.g. uipanels). but by default we don't, + % unless the panel.add() static constructor is + % used. + + if strcmp(op, 'register') + + argument_h_figure = data.h_figure; + i = 0; + while i < length(registeredPanels) + i = i + 1; + if registeredPanels(i).h_figure == argument_h_figure + delete(registeredPanels(i)); + i = 0; + end + end + + end + + % register the new panel + if isempty(registeredPanels) + registeredPanels = data; + else + registeredPanels(end + 1) = data; + end + + % debug output + % panel.debugmsg(['panel registered (' int2str(length(registeredPanels)) ' now registered)']); + + case 'unregister' + + % debug output + % panel.debugmsg(['on unregister, ' int2str(length(registeredPanels)) ' registered']); + + for r = 1:length(registeredPanels) + if registeredPanels(r) == data + registeredPanels = registeredPanels([1:r - 1 r + 1:end]); + + % debug output + % panel.debugmsg(['panel unregistered (' int2str(length(registeredPanels)) ' now registered)']); + + return + end + end + + % warn + warning('panel:AbsentOnCallbacksUnregister', 'panel was absent from the callbacks register when it tried to unregister itself'); + + case 'resize' + + argument_h_parent = data; + for r = 1:length(registeredPanels) + if registeredPanels(r).h_parent == argument_h_parent + registeredPanels(r).recomputeLayout([]); + end + end + + case 'recover' + + argument_h_figure = data; + out = []; + for r = 1:length(registeredPanels) + if registeredPanels(r).h_figure == argument_h_figure + if isempty(out) + out = registeredPanels(r); + else + out(end + 1) = registeredPanels(r); + end + end + end + + case 'delete' + + argument_h_figure = data; + i = 0; + while i < length(registeredPanels) + i = i + 1; + if registeredPanels(i).h_figure == argument_h_figure + delete(registeredPanels(i)); + i = 0; + end + end + + end + + end + + end + +end + +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% HELPERS +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function restore = dashstyle_line(fix, context) + + % get axis size in mm + h_line = fix.h; + h_axis = get(h_line, 'parent'); + u = get(h_axis, 'units'); + set(h_axis, 'units', 'norm'); + pos = get(h_axis, 'position'); + set(h_axis, 'units', u); + axis_in_mm = pos(3:4) .* context.size_in_mm; + + % recover data + xdata = get(h_line, 'xdata'); + ydata = get(h_line, 'ydata'); + zdata = get(h_line, 'zdata'); + linestyle = get(h_line, 'linestyle'); + marker = get(h_line, 'marker'); + + % empty restore + restore = []; + + % do not handle 3D + if ~isempty(zdata) + warning('panel:NoFixdash3D', 'panel cannot fixdash() a 3D line - no action taken'); + return + end + + % get range of axis + ax = axis(h_axis); + + % get scale in each dimension (mm per unit) + sc = axis_in_mm ./ (ax([2 4]) - ax([1 3])); + + % create empty line + data = NaN; + + % override linestyle + if ~isempty(fix.linestyle) + linestyle = fix.linestyle; + end + + % transcribe linestyle + linestyle = dashstyle_parse_linestyle(linestyle); + if isempty(linestyle) + return + end + + % scale + scale = 1; + dashes = linestyle * scale; + + % store for restore + restore.h_line = h_line; + restore.xdata = xdata; + restore.ydata = ydata; + + % create another, separate, line to overlay on the original + % line and render the fixed-up dashes. + restore.h_supp = copyobj(h_line, h_axis); + + % if the original line has markers, we'll have to create yet + % another separate line instance to represent them, because + % they shouldn't be "dashed", as it were. note that we don't + % currently attempt to get the z-order right for these + % new lines. + if ~isequal(marker, 'none') + restore.h_mark = copyobj(h_line, h_axis); + set(restore.h_mark, 'linestyle', 'none'); + set(restore.h_supp, 'marker', 'none'); + else + restore.h_mark = []; + end + + % hide the original line. this line remains in existence so + % that if there is a legend, it doesn't get messed up. + set(h_line, 'xdata', NaN, 'ydata', NaN); + + % extract pattern length + patlen = sum(dashes); + + % position within pattern is initially zero + pos = 0; + + % linedata + line_xy = complex(xdata, ydata); + + % for each line segment + while length(line_xy) > 1 + + % get line segment + xy = line_xy(1:2); + line_xy = line_xy(2:end); + + % any NaNs, and we're outta here + if any(isnan(xy)) + continue + end + + % get start etc. + O = xy(1); + V = xy(2) - xy(1); + + % get mm length of this line segment + d = sqrt(sum(([real(V) imag(V)] .* sc).^2)); + + % and mm unit vector + U = V / d; + + % generate a long-enough pattern for this segment + n = ceil((pos + d) / patlen); + pat = [0 cumsum(repmat(dashes, [1 n]))] - pos; + pos = d - (pat(end) - patlen); + pat = [pat(1:2:end - 1); pat(2:2:end)]; + + % trim spurious segments + pat = pat(:, any(pat >= 0) & any(pat <= d)); + + % skip if that's it + if isempty(pat) + continue + end + + % and reduce ones that are oversized + pat(1) = max(pat(1), 0); + pat(end) = min(pat(end), d); + + % finally, add these segments to the line data + seg = [O + pat * U; NaN(1, size(pat, 2))]; + data = [data seg(:).']; + + end + + % update line + set(restore.h_supp, 'xdata', real(data), 'ydata', imag(data), ... + 'linestyle', '-'); + +end + +function linestyle = dashstyle_parse_linestyle(linestyle) + + if isequal(linestyle, 'none') || isequal(linestyle, '-') + linestyle = []; + return + end + + while 1 + + % if numbers + if isnumeric(linestyle) + if ~isa(linestyle, 'double') || ~isrow(linestyle) || mod(length(linestyle), 2) ~= 0 + break + end + % no need to parse + return + end + + % else, must be char + if ~ischar(linestyle) || ~isrow(linestyle) + break + end + + % translate matlab non-standard codes into codes we can + % easily parse + switch linestyle + case ':' + linestyle = '.'; + case '--' + linestyle = '-'; + end + + % must be only - and . + if any(linestyle ~= '.' & linestyle ~= '-') + break + end + + % transcribe + c = linestyle; + linestyle = []; + for l = c + switch l + case '-' + linestyle = [linestyle 2 0.75]; + case '.' + linestyle = [linestyle 0.5 0.75]; + end + end + return + + end + + warning('panel:BadFixdashLinestyle', 'unusable linestyle in fixdash()'); + linestyle = []; + +end + +% MISCELLANEOUS + +function index = isin(list, value) + + for i = 1:length(list) + if strcmp(value, list{i}) + index = i; + return + end + end + + index = 0; + +end + +function dim = flippackdim(dim) + + % this function, used between arguments in a recursive call, + % causes the dim to be switched with each recurse, so that + % we build a grid, rather than a long, long row. + dim = 3 - dim; + +end + +% STRING PADDING FUNCTIONS + +function s = rpad(s, l) + + if nargin < 2 + l = 16; + end + + if length(s) < l + s = [s repmat(' ', 1, l - length(s))]; + end + +end + +function s = lpad(s, l) + + if nargin < 2 + l = 16; + end + + if length(s) < l + s = [repmat(' ', 1, l - length(s)) s]; + end + +end + +% HANDLE GRAPHICS HELPERS + +function h = getParentFigure(h) + + if strcmp(get(h, 'type'), 'figure') + return + else + h = getParentFigure(get(h, 'parent')); + end + +end + +function addHandleCallback(h, name, func) + + % % get current list of callbacks + % callbacks = get(h, name); + % + % % if empty, turn into a cell + % if isempty(callbacks) + % callbacks = {}; + % elseif iscell(callbacks) + % % only add ourselves once + % for c = 1:length(callbacks) + % if callbacks{c} == func + % return + % end + % end + % else + % callbacks = {callbacks}; + % end + % + % % and add ours (this is friendly, in case someone else has a + % % callback attached) + % callbacks{end+1} = func; + % + % % lay in + % set(h, name, callbacks); + + % the above isn't as simple as i thought - for now, we'll + % just stamp on any existing callbacks + set(h, name, func); + +end + +function store = storeAxisState(h) + + % LOCK TICKS AND LIMITS + % + % (LOCK TICKS) + % + % lock state so that the ticks and labels do not change when + % the figure is resized for printing. this is what the user + % will expect, which is why we go through this palaver. + % + % however, for fuck's sake. the following code illustrates + % an idiosyncrasy of matlab (i would call this an + % inconsistency, myself, but there you go). + % + % figure + % axis([0 1 0 1]) + % set(gca, 'ytick', [-1 0 1 2]) + % get(gca, 'yticklabel') + % set(gca, 'yticklabelmode', 'manual') + % + % now, resize the figure window. at least in R2011b, the + % tick labels change on the first resize event. presumably, + % this is because matlab treats the ticklabel value + % differently depending on if the ticklabelmode is auto or + % manual. if it's manual, the value is used as documented, + % and [0 1] is used to label [-1 0 1 2], cyclically. + % however, if the ticklabelmode is auto, and the ticks + % extend outside the figure, then the ticklabels are set + % sensibly, but the _value_ of ticklabel is not consistent + % with what it would need to be to get this tick labelling + % were the mode manual. and, in a final bizarre twist, this + % doesn't become evident until the resize event. i think + % this is a bug, no other way of looking at it; at best it's + % an inconsistency that is either tedious or impossible to + % work around in the general case. + % + % in any case, we have to lock the ticks to manual as we go + % through the print cycle, so that the ticks do not get + % changed if they were in automatic mode. but we mustn't fix + % the tick labels to manual, since if we do we may encounter + % this inconsistency and end up with the wrong tick labels + % in the print out. i can't, at time of writing, think of a + % case where we'd have to fix the tick labels to manual too. + % the possible cases are: + % + % ticks auto, labels auto: in this case, fixing the ticks to + % manual should be enough. + % + % ticks manual, labels auto: leave as is. + % + % ticks manual, labels manual: leave as is. + % + % the only other case is ticks auto, labels manual, which is + % a risky case to use, but in any case we can also fix the + % ticks to manual in that case. thus, our preferred solution + % is to always switch the ticks to manual, if they're not + % already, and otherwise leave things be. + % + % (LOCK LIMITS) + % + % the other thing that may get modified, if the user hasn't + % fixed it, is the axis limits. so we lock them too, any + % that are set to auto, and mark them for unlocking when the + % print is complete. + + store = ''; + + % manual-ise ticks on any axis where they are currently + % automatic, and indicate that we need to switch them back + % afterwards. + if strcmp(get(h, 'XTickMode'), 'auto') + store = [store 'X']; + set(h, 'XTickMode', 'manual'); + end + if strcmp(get(h, 'YTickMode'), 'auto') + store = [store 'Y']; + set(h, 'YTickMode', 'manual'); + end + if strcmp(get(h, 'ZTickMode'), 'auto') + store = [store 'Z']; + set(h, 'ZTickMode', 'manual'); + end + + % manual-ise limits on any axis where they are currently + % automatic, and indicate that we need to switch them back + % afterwards. + if strcmp(get(h, 'XLimMode'), 'auto') + store = [store 'x']; + set(h, 'XLimMode', 'manual'); + end + if strcmp(get(h, 'YLimMode'), 'auto') + store = [store 'y']; + set(h, 'YLimMode', 'manual'); + end + if strcmp(get(h, 'ZLimMode'), 'auto') + store = [store 'z']; + set(h, 'ZLimMode', 'manual'); + end + + % % OLD CODE OBSOLETED 25/01/12 - see notes above + % + % % store current state + % store.XTick = get(h, 'XTick'); + % store.XTickMode = get(h, 'XTickMode'); + % store.XTickLabel = get(h, 'XTickLabel'); + % store.XTickLabelMode = get(h, 'XTickLabelMode'); + % store.YTickMode = get(h, 'YTickMode'); + % store.YTick = get(h, 'YTick'); + % store.YTickLabel = get(h, 'YTickLabel'); + % store.YTickLabelMode = get(h, 'YTickLabelMode'); + % store.ZTick = get(h, 'ZTick'); + % store.ZTickMode = get(h, 'ZTickMode'); + % store.ZTickLabel = get(h, 'ZTickLabel'); + % store.ZTickLabelMode = get(h, 'ZTickLabelMode'); + % + % % lock state to manual + % set(h, 'XTickLabelMode', 'manual'); + % set(h, 'XTickMode', 'manual'); + % set(h, 'YTickLabelMode', 'manual'); + % set(h, 'YTickMode', 'manual'); + % set(h, 'ZTickLabelMode', 'manual'); + % set(h, 'ZTickMode', 'manual'); + +end + +function restoreAxisState(h, store) + + % unmanualise + for item = store + switch item + case {'X' 'Y' 'Z'} + set(h, [item 'TickMode'], 'auto'); + case {'x' 'y' 'z'} + set(h, [upper(item) 'TickMode'], 'auto'); + end + end + + % % OLD CODE OBSOLETED 25/01/12 - see notes above + % + % % restore passed state + % set(h, 'XTick', store.XTick); + % set(h, 'XTickMode', store.XTickMode); + % set(h, 'XTickLabel', store.XTickLabel); + % set(h, 'XTickLabelMode', store.XTickLabelMode); + % set(h, 'YTick', store.YTick); + % set(h, 'YTickMode', store.YTickMode); + % set(h, 'YTickLabel', store.YTickLabel); + % set(h, 'YTickLabelMode', store.YTickLabelMode); + % set(h, 'ZTick', store.ZTick); + % set(h, 'ZTickMode', store.ZTickMode); + % set(h, 'ZTickLabel', store.ZTickLabel); + % set(h, 'ZTickLabelMode', store.ZTickLabelMode); + +end + +% DIM AND EDGE HANDLING + +% we describe each edge of a panel in terms of "dim" (1 or +% 2, horizontal or vertical) and "edge" (1 or 2, former or +% latter). together, [dim edge] is an "edgespec". + +function s = edgestr(edgespec) + + s = 'lbrt'; + s = s(edgeindex(edgespec)); + +end + +function i = edgeindex(edgespec) + + % edge indices. margins are stored as [l b r t] but + % dims are packed left to right and top to bottom, so + % relationship between 'dim' and 'end' and index into + % margin is non-trivial. we call the index into the margin + % the "edgeindex". an "edgespec" is just [dim end], in a + % single array. + i = [1 3; 4 2]; + i = i(edgespec(1), edgespec(2)); + +end + +% VARIABLE TYPE HELPERS + +function val = validate_par(val, argtext, varargin) + + % this helper validates arguments to some functions in the + % main body + + for n = 1:length(varargin) + + % get validation constraint + arg = varargin{n}; + + % handle string list + if iscell(arg) + % string list + if ~isin(arg, val) + error('panel:InvalidArgument', ... + ['invalid argument "' argtext '", "' val '" is not a recognised data value for this option']); + end + continue + end + + % handle strings + if isstring(arg) + switch arg + case 'empty' + if ~isempty(val) + error('panel:InvalidArgument', ... + ['invalid argument "' argtext '", option does not expect any data']); + end + case 'dimension' + if ~isdimension(val) + error('panel:InvalidArgument', ... + ['invalid argument "' argtext '", option expects a dimension']); + end + case 'scalar' + if ~(isnumeric(val) && isscalar(val) && ~isnan(val)) + error('panel:InvalidArgument', ... + ['invalid argument "' argtext '", option expects a scalar value']); + end + case 'nonneg' + if any(val(:) < 0) + error('panel:InvalidArgument', ... + ['invalid argument "' argtext '", option expects non-negative values only']); + end + case 'integer' + if any(val(:) ~= round(val(:))) + error('panel:InvalidArgument', ... + ['invalid argument "' argtext '", option expects integer values only']); + end + end + continue + end + + % handle numeric range + if isnumeric(arg) && isofsize(arg, [1 2]) + if any(val(:) < arg(1)) || any(val(:) > arg(2)) + error('panel:InvalidArgument', ... + ['invalid argument "' argtext '", option data must be between ' num2str(arg(1)) ' and ' num2str(arg(2))]); + end + continue + end + + % not recognised + arg; + error('panel:InternalError', 'internal error - bad argument to validate_par (above)'); + + end + +end + +function b = checkpar(value, mn, mx) + + b = isscalar(value) && isnumeric(value) && ~isnan(value); + if b + if nargin >= 2 + b = b && value >= mn; + end + if nargin >= 3 + b = b && value <= mx; + end + end + +end + +function b = isintegral(v) + + b = all(all(v == round(v))); + +end + +function b = isstring(value) + + sz = size(value); + b = ischar(value) && length(sz) == 2 && sz(1) == 1 && sz(2) >= 1; + +end + +function b = isdimension(value) + + b = isa(value, 'double') && (isscalar(value) || isofsize(value, [1 4])); + +end + +function b = isscalardimension(value) + + b = isa(value, 'double') && isscalar(value); + +end + +function b = isofsize(value, siz) + + sz = size(value); + b = length(sz) == length(siz) && all(sz == siz); + +end + +function b = isaxis(h) + + b = ishandle(h) && strcmp(get(h, 'type'), 'axes'); + +end + +function validate_packspec(packspec) + + % stretchable + if isempty(packspec) + return + end + + % scalar + if isa(packspec, 'double') && isscalar(packspec) + + % fraction + if packspec > 0 && packspec <= 1 + return + end + + % percentage + if packspec > 1 && packspec <= 100 + return + end + + end + + % fixed + if iscell(packspec) && isscalar(packspec) + + % delve + d = packspec{1}; + if isa(d, 'double') && isscalar(d) && d > 0 + return + end + + end + + % abs + if isa(packspec, 'double') && isofsize(packspec, [1 4]) && all(packspec(3:4) > 0) + return + end + + % otherwise, bad form + error('panel:BadPackingSpecifier', 'the packing specifier was not valid - see help panel/pack'); + +end diff --git a/lib/utils/plot_power_spectra_of_GLM_residuals.m b/lib/utils/plot_power_spectra_of_GLM_residuals.m index cb4b1b90..689827e7 100644 --- a/lib/utils/plot_power_spectra_of_GLM_residuals.m +++ b/lib/utils/plot_power_spectra_of_GLM_residuals.m @@ -1,191 +1,191 @@ function plot_power_spectra_of_GLM_residuals(path_to_results, TR, cutoff_freq, assumed_exper_freq, true_exper_freq) - % -By Wiktor Olszowy, University of Cambridge, olszowyw@gmail.com - % - % -Written following study - % 'Accurate autocorrelation modeling substantially improves fMRI reliability' - % -https://www.nature.com/articles/s41467-019-09230-w.pdf - % -May 2018 - % - % -Given fMRI task results in AFNI, FSL or SPM, - % this script plots power spectra of GLM residuals. - % -If there is strong structure visible in the GLM residuals - % the power spectra are not flat), the first level results are likely confounded. - - % -tested on Linux - % -you need on your path >= MATLAB 2017b, AFNI and FSL - - % -specify the default values for the cutoff frequency used by the high-pass filter, - % -for the assumed experimental design frequency - % and for the true experimental design frequency; - % -10 chosen, as it is beyond the plotted frequencies - - if ~exist('cutoff_freq', 'var') - cutoff_freq = 10; - end - if ~exist('assumed_exper_freq', 'var') - assumed_exper_freq = 10; - end - if ~exist('true_exper_freq', 'var') - true_exper_freq = 10; - end - - Fontsize = 10; - - % -Fast Fourier Transform (FFT) will pad the voxel-wise time series - % to that length with trailing zeros (if no. of time points lower) - % or truncate to that length (if no. of time points higher) - fft_n = 512; - - initial_path = pwd; - cd(path_to_results); - - % -read GLM residuals - - % -for AFNI - AFNI_res4d_name = dir('whitened_errts.*.BRIK'); - if length(AFNI_res4d_name) > 0 - AFNI_res4d_name = AFNI_res4d_name.name; - system(['3dcalc -a ' AFNI_res4d_name ' -expr "a" -prefix res4d.nii']); - res4d = niftiread('res4d.nii'); - - % -for FSL - elseif exist('stats/res4d.nii.gz', 'file') == 2 - res4d = niftiread('stats/res4d.nii.gz'); - elseif exist('stats/res4d.nii', 'file') == 2 - res4d = niftiread('stats/res4d.nii'); - - % -for SPM - elseif exist('Res_0001.nii', 'file') == 2 - - SPM_res4d_name = dir('Res_*.nii'); - SPM_res4d_all = ''; - - % -in case we get a crash, we rely on FSL functions - try - SPM_res4d_all = char({SPM_res4d_name.name}'); - spm_file_merge(SPM_res4d_all, 'res4d.nii'); - res4d = spm_read_vols(spm_vol('res4d.nii')); - - catch - for i = 1:length(SPM_res4d_name) - SPM_res4d_all = [SPM_res4d_all ' ' SPM_res4d_name(i).name]; - end - system(['fslmerge -t res4d ' SPM_res4d_all]); - res4d = niftiread('res4d.nii.gz'); - - end - - else - - disp(['No GLM residuals found! ', ... - 'If you run SPM, remember to put command ', ... - 'VRes = spm_write_residuals(SPM, NaN) at the end of the SPM script. ', ... - 'Otherwise, SPM by default deletes the GLM residuals.']); - return + % -By Wiktor Olszowy, University of Cambridge, olszowyw@gmail.com + % + % -Written following study + % 'Accurate autocorrelation modeling substantially improves fMRI reliability' + % -https://www.nature.com/articles/s41467-019-09230-w.pdf + % -May 2018 + % + % -Given fMRI task results in AFNI, FSL or SPM, + % this script plots power spectra of GLM residuals. + % -If there is strong structure visible in the GLM residuals + % the power spectra are not flat), the first level results are likely confounded. + + % -tested on Linux + % -you need on your path >= MATLAB 2017b, AFNI and FSL + + % -specify the default values for the cutoff frequency used by the high-pass filter, + % -for the assumed experimental design frequency + % and for the true experimental design frequency; + % -10 chosen, as it is beyond the plotted frequencies + + if ~exist('cutoff_freq', 'var') + cutoff_freq = 10; + end + if ~exist('assumed_exper_freq', 'var') + assumed_exper_freq = 10; + end + if ~exist('true_exper_freq', 'var') + true_exper_freq = 10; + end + + Fontsize = 10; + + % -Fast Fourier Transform (FFT) will pad the voxel-wise time series + % to that length with trailing zeros (if no. of time points lower) + % or truncate to that length (if no. of time points higher) + fft_n = 512; + + initial_path = pwd; + cd(path_to_results); + + % -read GLM residuals + + % -for AFNI + AFNI_res4d_name = dir('whitened_errts.*.BRIK'); + if length(AFNI_res4d_name) > 0 + AFNI_res4d_name = AFNI_res4d_name.name; + system(['3dcalc -a ' AFNI_res4d_name ' -expr "a" -prefix res4d.nii']); + res4d = niftiread('res4d.nii'); + + % -for FSL + elseif exist('stats/res4d.nii.gz', 'file') == 2 + res4d = niftiread('stats/res4d.nii.gz'); + elseif exist('stats/res4d.nii', 'file') == 2 + res4d = niftiread('stats/res4d.nii'); + + % -for SPM + elseif exist('Res_0001.nii', 'file') == 2 + + SPM_res4d_name = dir('Res_*.nii'); + SPM_res4d_all = ''; + + % -in case we get a crash, we rely on FSL functions + try + SPM_res4d_all = char({SPM_res4d_name.name}'); + spm_file_merge(SPM_res4d_all, 'res4d.nii'); + res4d = spm_read_vols(spm_vol('res4d.nii')); + + catch + for i = 1:length(SPM_res4d_name) + SPM_res4d_all = [SPM_res4d_all ' ' SPM_res4d_name(i).name]; + end + system(['fslmerge -t res4d ' SPM_res4d_all]); + res4d = niftiread('res4d.nii.gz'); end - % -calculate the power spectra + else + + disp(['No GLM residuals found! ', ... + 'If you run SPM, remember to put command ', ... + 'VRes = spm_write_residuals(SPM, NaN) at the end of the SPM script. ', ... + 'Otherwise, SPM by default deletes the GLM residuals.']); + return - dims = size(res4d); - power_spectra_of_GLM_residuals = zeros(fft_n, 1); - no_of_brain_voxels = 0; + end - for i1 = 1:dims(1) + % -calculate the power spectra - for i2 = 1:dims(2) + dims = size(res4d); + power_spectra_of_GLM_residuals = zeros(fft_n, 1); + no_of_brain_voxels = 0; - for i3 = 1:dims(3) + for i1 = 1:dims(1) - ts = squeeze(res4d(i1, i2, i3, :)); + for i2 = 1:dims(2) - if sum(isnan(ts)) == 0 + for i3 = 1:dims(3) - if std(ts) ~= 0 + ts = squeeze(res4d(i1, i2, i3, :)); - % -make signal variance equal to 1 - ts = ts / (std(ts) + eps); + if sum(isnan(ts)) == 0 - % -compute the discrete Fourier transform (DFT) - DFT = fft(ts, fft_n); + if std(ts) ~= 0 - power_spectra_of_GLM_residuals = power_spectra_of_GLM_residuals + ... - ((abs(DFT)).^2) / min(dims(4), fft_n); + % -make signal variance equal to 1 + ts = ts / (std(ts) + eps); - no_of_brain_voxels = no_of_brain_voxels + 1; + % -compute the discrete Fourier transform (DFT) + DFT = fft(ts, fft_n); - end + power_spectra_of_GLM_residuals = power_spectra_of_GLM_residuals + ... + ((abs(DFT)).^2) / min(dims(4), fft_n); - end + no_of_brain_voxels = no_of_brain_voxels + 1; - end + end end - end + end - % -average power spectra over all brain voxels - power_spectra_of_GLM_residuals = power_spectra_of_GLM_residuals / no_of_brain_voxels; - - % -save the power spectra - clear res4d; - save('power_spectra_of_GLM_residuals.mat'); - - % -make the plot - figure('rend', 'painters', 'pos', [0 0 600 400], 'Visible', 'on'); - hold on; - - f = linspace(0, 0.5 / TR, 257); - max_y = max(power_spectra_of_GLM_residuals); - h1 = plot(f, power_spectra_of_GLM_residuals(1:257), 'r'); - h4 = plot(cutoff_freq, 0, 'k*'); - h5 = plot(cutoff_freq, max_y, 'k*'); - h6 = plot(assumed_exper_freq, 0, 'c*'); - h7 = plot(assumed_exper_freq, max_y, 'c*'); - h8 = plot(true_exper_freq, 0, 'm*'); - h9 = plot(true_exper_freq, max_y, 'm*'); - h10 = plot([0 0.5 / TR], [1 1], 'k--'); - hx = xlabel({' ', 'Frequency [Hz]', ' '}); - htitle = title('Power spectra of GLM residuals', 'interpreter', 'none'); - - xlim([0 0.5 / TR]); - ylim([0 max_y]); - - set([h1 h10], 'LineWidth', 1.25); - set([hx htitle], 'FontSize', Fontsize); - set(gca, 'XTick', linspace(0, 0.5 / TR, 6)); - set(gca, 'XTickLabel', round(linspace(0, 0.5 / TR, 6), 2)); - set(gca, 'FontSize', Fontsize); - - location = 'southeast'; - if (power_spectra_of_GLM_residuals(257) < 0) || (max_y > 2) - location = 'northeast'; end - to_plot = [h1 h10 h4 h6 h8]; + end + + % -average power spectra over all brain voxels + power_spectra_of_GLM_residuals = power_spectra_of_GLM_residuals / no_of_brain_voxels; + + % -save the power spectra + clear res4d; + save('power_spectra_of_GLM_residuals.mat'); + + % -make the plot + figure('rend', 'painters', 'pos', [0 0 600 400], 'Visible', 'on'); + hold on; + + f = linspace(0, 0.5 / TR, 257); + max_y = max(power_spectra_of_GLM_residuals); + h1 = plot(f, power_spectra_of_GLM_residuals(1:257), 'r'); + h4 = plot(cutoff_freq, 0, 'k*'); + h5 = plot(cutoff_freq, max_y, 'k*'); + h6 = plot(assumed_exper_freq, 0, 'c*'); + h7 = plot(assumed_exper_freq, max_y, 'c*'); + h8 = plot(true_exper_freq, 0, 'm*'); + h9 = plot(true_exper_freq, max_y, 'm*'); + h10 = plot([0 0.5 / TR], [1 1], 'k--'); + hx = xlabel({' ', 'Frequency [Hz]', ' '}); + htitle = title('Power spectra of GLM residuals', 'interpreter', 'none'); + + xlim([0 0.5 / TR]); + ylim([0 max_y]); + + set([h1 h10], 'LineWidth', 1.25); + set([hx htitle], 'FontSize', Fontsize); + set(gca, 'XTick', linspace(0, 0.5 / TR, 6)); + set(gca, 'XTickLabel', round(linspace(0, 0.5 / TR, 6), 2)); + set(gca, 'FontSize', Fontsize); + + location = 'southeast'; + if (power_spectra_of_GLM_residuals(257) < 0) || (max_y > 2) + location = 'northeast'; + end + + to_plot = [h1 h10 h4 h6 h8]; + legend_content = { ... + 'Actual power spectrum', ... + 'Ideal power spectrum', ... + 'High pass filter frequency cutoff', ... + 'Assumed design frequency', ... + 'True design frequency'}; + + if any([cutoff_freq, assumed_exper_freq, true_exper_freq] == 10) + to_plot = [h1 h10]; legend_content = { ... 'Actual power spectrum', ... - 'Ideal power spectrum', ... - 'High pass filter frequency cutoff', ... - 'Assumed design frequency', ... - 'True design frequency'}; - - if any([cutoff_freq, assumed_exper_freq, true_exper_freq] == 10) - to_plot = [h1 h10]; - legend_content = { ... - 'Actual power spectrum', ... - 'Ideal power spectrum'}; - end + 'Ideal power spectrum'}; + end - legend(to_plot, legend_content, ... - 'box', 'off', ... - 'FontSize', Fontsize, ... - 'Location', location); + legend(to_plot, legend_content, ... + 'box', 'off', ... + 'FontSize', Fontsize, ... + 'Location', location); - figname = 'power_spectra_of_GLM_residuals'; - print (figname, '-dpng'); + figname = 'power_spectra_of_GLM_residuals'; + print (figname, '-dpng'); - cd(initial_path); + cd(initial_path); end diff --git a/lib/utils/resize_img.m b/lib/utils/resize_img.m index fdd5407d..0e23418e 100644 --- a/lib/utils/resize_img.m +++ b/lib/utils/resize_img.m @@ -1,149 +1,149 @@ function resize_img(imnames, Voxdim, BB, ismask) - % resize_img -- resample images to have specified voxel dims and BBox - % - % resize_img(imnames, voxdim, bb, ismask) - % - % Output images will be prefixed with 'r', and will have voxel dimensions - % equal to voxdim. Use NaNs to determine voxdims from transformation matrix - % of input image(s). - % If bb == nan(2,3), bounding box will include entire original image - % Origin will move appropriately. Use world_bb to compute bounding box from - % a different image. - % - % Pass ismask=true to re-round binary mask values (avoid - % growing/shrinking masks due to linear interp) - % - % See also voxdim, world_bb + % resize_img -- resample images to have specified voxel dims and BBox + % + % resize_img(imnames, voxdim, bb, ismask) + % + % Output images will be prefixed with 'r', and will have voxel dimensions + % equal to voxdim. Use NaNs to determine voxdims from transformation matrix + % of input image(s). + % If bb == nan(2,3), bounding box will include entire original image + % Origin will move appropriately. Use world_bb to compute bounding box from + % a different image. + % + % Pass ismask=true to re-round binary mask values (avoid + % growing/shrinking masks due to linear interp) + % + % See also voxdim, world_bb - % Based on John Ashburner's reorient.m - % http://www.sph.umich.edu/~nichols/JohnsGems.html#Gem7 - % http://www.sph.umich.edu/~nichols/JohnsGems5.html#Gem2 - % Adapted by Ged Ridgway -- email bugs to drc.spm@gmail.com + % Based on John Ashburner's reorient.m + % http://www.sph.umich.edu/~nichols/JohnsGems.html#Gem7 + % http://www.sph.umich.edu/~nichols/JohnsGems5.html#Gem2 + % Adapted by Ged Ridgway -- email bugs to drc.spm@gmail.com - % This version doesn't check spm_flip_analyze_images -- the handedness of - % the output image and matrix should match those of the input. + % This version doesn't check spm_flip_analyze_images -- the handedness of + % the output image and matrix should match those of the input. - % DONWLOADED FROM ON THE 2021 02 17 - % https://blogs.warwick.ac.uk/nichols/entry/spm5_gem_3/ + % DONWLOADED FROM ON THE 2021 02 17 + % https://blogs.warwick.ac.uk/nichols/entry/spm5_gem_3/ - % Check spm version: - if exist('spm_select', 'file') % should be true for spm5 - spm5 = 1; - elseif exist('spm_get', 'file') % should be true for spm2 - spm5 = 0; - else - error('Can''t find spm_get or spm_select; please add SPM to path'); - end + % Check spm version: + if exist('spm_select', 'file') % should be true for spm5 + spm5 = 1; + elseif exist('spm_get', 'file') % should be true for spm2 + spm5 = 0; + else + error('Can''t find spm_get or spm_select; please add SPM to path'); + end - % spm_defaults; + % spm_defaults; - % prompt for missing arguments - if ~exist('imnames', 'var') || isempty(char(imnames)) - if spm5 - imnames = spm_select(inf, 'image', 'Choose images to resize'); - else - imnames = spm_get(inf, 'img', 'Choose images to resize'); - end - end - % check if inter fig already open, don't close later if so... - Fint = spm_figure('FindWin', 'Interactive'); - Fnew = []; - if ~exist('Voxdim', 'var') || isempty(Voxdim) - Fnew = spm_figure('GetWin', 'Interactive'); - Voxdim = spm_input('Vox Dims (NaN for "as input")? ', ... - '+1', 'e', '[nan nan nan]', 3); - end - if ~exist('BB', 'var') || isempty(BB) - Fnew = spm_figure('GetWin', 'Interactive'); - BB = spm_input('Bound Box (NaN => original)? ', ... - '+1', 'e', '[nan nan nan; nan nan nan]', [2 3]); - end - if ~exist('ismask', 'var') - ismask = false; - end - if isempty(ismask) - ismask = false; + % prompt for missing arguments + if ~exist('imnames', 'var') || isempty(char(imnames)) + if spm5 + imnames = spm_select(inf, 'image', 'Choose images to resize'); + else + imnames = spm_get(inf, 'img', 'Choose images to resize'); end + end + % check if inter fig already open, don't close later if so... + Fint = spm_figure('FindWin', 'Interactive'); + Fnew = []; + if ~exist('Voxdim', 'var') || isempty(Voxdim) + Fnew = spm_figure('GetWin', 'Interactive'); + Voxdim = spm_input('Vox Dims (NaN for "as input")? ', ... + '+1', 'e', '[nan nan nan]', 3); + end + if ~exist('BB', 'var') || isempty(BB) + Fnew = spm_figure('GetWin', 'Interactive'); + BB = spm_input('Bound Box (NaN => original)? ', ... + '+1', 'e', '[nan nan nan; nan nan nan]', [2 3]); + end + if ~exist('ismask', 'var') + ismask = false; + end + if isempty(ismask) + ismask = false; + end - % reslice images one-by-one - vols = spm_vol(imnames); - for V = vols' - % (copy to allow defaulting of NaNs differently for each volume) - voxdim = Voxdim; - bb = BB; - % default voxdim to current volume's voxdim, (from mat parameters) - if any(isnan(voxdim)) - vprm = spm_imatrix(V.mat); - vvoxdim = vprm(7:9); - voxdim(isnan(voxdim)) = vvoxdim(isnan(voxdim)); - end - voxdim = voxdim(:)'; + % reslice images one-by-one + vols = spm_vol(imnames); + for V = vols' + % (copy to allow defaulting of NaNs differently for each volume) + voxdim = Voxdim; + bb = BB; + % default voxdim to current volume's voxdim, (from mat parameters) + if any(isnan(voxdim)) + vprm = spm_imatrix(V.mat); + vvoxdim = vprm(7:9); + voxdim(isnan(voxdim)) = vvoxdim(isnan(voxdim)); + end + voxdim = voxdim(:)'; - mn = bb(1, :); - mx = bb(2, :); - % default BB to current volume's - if any(isnan(bb(:))) - vbb = world_bb(V); - vmn = vbb(1, :); - vmx = vbb(2, :); - mn(isnan(mn)) = vmn(isnan(mn)); - mx(isnan(mx)) = vmx(isnan(mx)); - end + mn = bb(1, :); + mx = bb(2, :); + % default BB to current volume's + if any(isnan(bb(:))) + vbb = world_bb(V); + vmn = vbb(1, :); + vmx = vbb(2, :); + mn(isnan(mn)) = vmn(isnan(mn)); + mx(isnan(mx)) = vmx(isnan(mx)); + end - % voxel [1 1 1] of output should map to BB mn - % (the combination of matrices below first maps [1 1 1] to [0 0 0]) - mat = spm_matrix([mn 0 0 0 voxdim]) * spm_matrix([-1 -1 -1]); - % voxel-coords of BB mx gives number of voxels required - % (round up if more than a tenth of a voxel over) - imgdim = ceil(mat \ [mx 1]' - 0.1)'; + % voxel [1 1 1] of output should map to BB mn + % (the combination of matrices below first maps [1 1 1] to [0 0 0]) + mat = spm_matrix([mn 0 0 0 voxdim]) * spm_matrix([-1 -1 -1]); + % voxel-coords of BB mx gives number of voxels required + % (round up if more than a tenth of a voxel over) + imgdim = ceil(mat \ [mx 1]' - 0.1)'; - % output image - VO = V; - [pth, nam, ext] = fileparts(V.fname); - VO.fname = fullfile(pth, ['r' nam ext]); - VO.dim(1:3) = imgdim(1:3); - VO.mat = mat; - VO = spm_create_vol(VO); - spm_progress_bar('Init', imgdim(3), 'reslicing...', 'planes completed'); - for i = 1:imgdim(3) - M = inv(spm_matrix([0 0 -i]) * inv(VO.mat) * V.mat); - img = spm_slice_vol(V, M, imgdim(1:2), 1); % (linear interp) - if ismask - img = round(img); - end - spm_write_plane(VO, img, i); - spm_progress_bar('Set', i); - end - spm_progress_bar('Clear'); - end - % call spm_close_vol if spm2 - if ~spm5 - spm_close_vol(VO); - end - if isempty(Fint) && ~isempty(Fnew) - % interactive figure was opened by this script, so close it again. - close(Fnew); + % output image + VO = V; + [pth, nam, ext] = fileparts(V.fname); + VO.fname = fullfile(pth, ['r' nam ext]); + VO.dim(1:3) = imgdim(1:3); + VO.mat = mat; + VO = spm_create_vol(VO); + spm_progress_bar('Init', imgdim(3), 'reslicing...', 'planes completed'); + for i = 1:imgdim(3) + M = inv(spm_matrix([0 0 -i]) * inv(VO.mat) * V.mat); + img = spm_slice_vol(V, M, imgdim(1:2), 1); % (linear interp) + if ismask + img = round(img); + end + spm_write_plane(VO, img, i); + spm_progress_bar('Set', i); end - disp('Done.'); + spm_progress_bar('Clear'); + end + % call spm_close_vol if spm2 + if ~spm5 + spm_close_vol(VO); + end + if isempty(Fint) && ~isempty(Fnew) + % interactive figure was opened by this script, so close it again. + close(Fnew); + end + disp('Done.'); - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function bb = world_bb(V) - % world-bb -- get bounding box in world (mm) coordinates + % world-bb -- get bounding box in world (mm) coordinates - d = V.dim(1:3); - % corners in voxel-space - c = [1 1 1 1 - 1 1 d(3) 1 - 1 d(2) 1 1 - 1 d(2) d(3) 1 - d(1) 1 1 1 - d(1) 1 d(3) 1 - d(1) d(2) 1 1 - d(1) d(2) d(3) 1]'; - % corners in world-space - tc = V.mat(1:3, 1:4) * c; + d = V.dim(1:3); + % corners in voxel-space + c = [1 1 1 1 + 1 1 d(3) 1 + 1 d(2) 1 1 + 1 d(2) d(3) 1 + d(1) 1 1 1 + d(1) 1 d(3) 1 + d(1) d(2) 1 1 + d(1) d(2) d(3) 1]'; + % corners in world-space + tc = V.mat(1:3, 1:4) * c; - % bounding box (world) min and max - mn = min(tc, [], 2)'; - mx = max(tc, [], 2)'; - bb = [mn; mx]; + % bounding box (world) min and max + mn = min(tc, [], 2)'; + mx = max(tc, [], 2)'; + bb = [mn; mx]; diff --git a/manualTests/test_setBatchCoregistrationFmap.m b/manualTests/test_setBatchCoregistrationFmap.m index 4d055052..b793c1a4 100644 --- a/manualTests/test_setBatchCoregistrationFmap.m +++ b/manualTests/test_setBatchCoregistrationFmap.m @@ -19,7 +19,7 @@ function test_setBatchCoregistrationFmapBasic() [~, opt, BIDS] = getData(opt); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchCoregistrationFmap(matlabbatch, BIDS, opt, subID); directory = fullfile(opt.derivativesDir, ... diff --git a/manualTests/test_setBatchCreateVDMs.m b/manualTests/test_setBatchCreateVDMs.m index e87e6350..01547f35 100644 --- a/manualTests/test_setBatchCreateVDMs.m +++ b/manualTests/test_setBatchCreateVDMs.m @@ -20,7 +20,7 @@ function test_setBatchCreateVDMsBasic() [~, opt, BIDS] = getData(opt); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchCreateVDMs(matlabbatch, BIDS, opt, subID); % matlabbatch{1}.spm.tools.fieldmap.calculatevdm.subj diff --git a/miss_hit.cfg b/miss_hit.cfg index 3c2173ce..95640789 100644 --- a/miss_hit.cfg +++ b/miss_hit.cfg @@ -19,5 +19,5 @@ tab_width: 2 # metrics limit for the code quality (https://florianschanda.github.io/miss_hit/metrics.html) metric "cnest": limit 4 metric "file_length": limit 400 -metric "cyc": limit 12 +metric "cyc": limit 18 metric "parameters": limit 6 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 578e596a..667fbf62 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ Sphinx sphinxcontrib-matlabdomain sphinxcontrib-napoleon sphinx_rtd_theme -miss_hit==0.9.20 \ No newline at end of file +miss_hit==0.9.22 +pre-commit \ No newline at end of file diff --git a/src/QA/anatomicalQA.m b/src/QA/anatomicalQA.m index 3e689e8c..6251603b 100644 --- a/src/QA/anatomicalQA.m +++ b/src/QA/anatomicalQA.m @@ -28,11 +28,11 @@ function anatomicalQA(opt) parfor iSub = 1:numel(opt.subjects) - subID = opt.subjects{iSub}; + subLabel = opt.subjects{iSub}; %#ok<*PFBNS> - printProcessingSubject(iSub, subID); + printProcessingSubject(iSub, subLabel); - [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt); + [anatImage, anatDataDir] = getAnatFilename(BIDS, subLabel, opt); % get grey and white matter tissue probability maps TPMs = validationInputFile(anatDataDir, anatImage, 'c[12]'); diff --git a/src/QA/anatomicalQALesion.m b/src/QA/anatomicalQALesion.m new file mode 100755 index 00000000..8330e9bf --- /dev/null +++ b/src/QA/anatomicalQALesion.m @@ -0,0 +1,59 @@ +function anatomicalQALesion(opt) + % + % Computes several metrics for anatomical image. + % Modify this version for ALI toolbox TO DO + % + % USAGE:: + % + % anatomicalQA(opt) + % + % :param opt: Options chosen for the analysis. See ``checkOptions()``. + % :type opt: structure + % + % (C) Copyright 2020 CPP_SPM developers + + if isOctave() + warning('\nanatomicalQA is not yet supported on Octave. This step will be skipped.'); + return + end + + % if input has no opt, load the opt.mat file + if nargin < 1 + opt = []; + end + opt = loadAndCheckOptions(opt); + + [BIDS, opt] = getData(opt); + + fprintf(1, ' ANATOMICAL: QUALITY CONTROL\n\n'); + + parfor iSub = 1:numel(opt.subjects) + + subID = opt.subjects{iSub}; + + printProcessingSubject(iSub, subID); + + [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt); + + % get grey and white matter tissue probability maps + TPMs = validationInputFile(anatDataDir, anatImage, 'c[12]'); + + % sanity check that all images are in the same space. + anatImage = fullfile(anatDataDir, anatImage); + volumesToCheck = {anatImage; TPMs(1, :); TPMs(2, :)}; + spm_check_orientations(spm_vol(char(volumesToCheck))); + + % Basic QA for anatomical data is to get SNR, CNR, FBER and Entropy + % This is useful to check coregistration worked fine + anatQA = spmup_anatQA(anatImage, TPMs(1, :), TPMs(2, :)); %#ok<*NASGU> + + anatQA.avgDistToSurf = spmup_comp_dist2surf(anatImage); + + spm_jsonwrite( ... + strrep(anatImage, '.nii', '_qa.json'), ... + anatQA, ... + struct('indent', ' ')); + + end + +end diff --git a/src/QA/functionalQA.m b/src/QA/functionalQA.m index f74ff38c..887e9274 100644 --- a/src/QA/functionalQA.m +++ b/src/QA/functionalQA.m @@ -41,12 +41,12 @@ function functionalQA(opt) for iSub = 1:numel(opt.subjects) - subID = opt.subjects{iSub}; + subLabel = opt.subjects{iSub}; - printProcessingSubject(iSub, subID); + printProcessingSubject(iSub, subLabel); % get grey and white matter and csf tissue probability maps - [anatImage, anatDataDir] = getAnatFilename(BIDS, subID, opt); + [anatImage, anatDataDir] = getAnatFilename(BIDS, subLabel, opt); TPMs = validationInputFile(anatDataDir, anatImage, 'rc[123]'); % load metrics from anat QA @@ -55,19 +55,19 @@ function functionalQA(opt) anatDataDir, ... strrep(anatImage, '.nii', '_qa.json'))); - [sessions, nbSessions] = getInfo(BIDS, subID, opt, 'Sessions'); + [sessions, nbSessions] = getInfo(BIDS, subLabel, opt, 'Sessions'); for iSes = 1:nbSessions % get all runs for that subject across all sessions - [runs, nbRuns] = getInfo(BIDS, subID, opt, 'Runs', sessions{iSes}); + [runs, nbRuns] = getInfo(BIDS, subLabel, opt, 'Runs', sessions{iSes}); for iRun = 1:nbRuns % get the filename for this bold run for this task [fileName, subFuncDataDir] = getBoldFilename( ... BIDS, ... - subID, ... + subLabel, ... sessions{iSes}, ... runs{iRun}, ... opt); @@ -97,7 +97,12 @@ function functionalQA(opt) outputFiles = spmup_first_level_qa( ... funcImage, ... - 'Voltera', 'on', ... + 'MotionParameters', opt.QA.func.Motion, ... + 'FramewiseDisplacement', opt.QA.func.FD, ... + 'Globals', opt.QA.func.Globals, ... + 'Movie', opt.QA.func.Movie, ... + 'Basics', opt.QA.func.Basics, ... + 'Voltera', opt.QA.func.Voltera, ... 'Radius', anatQA.avgDistToSurf); movefile( ... @@ -122,7 +127,7 @@ function functionalQA(opt) % horrible hack to prevent the "abrupt" way spmup_volumecorr crashes % if nansum is not there - if exist('nansum', 'file') == 2 + if opt.QA.func.carpetPlot && exist('nansum', 'file') == 2 spmup_timeseriesplot(funcImage, TPMs(1, :), TPMs(2, :), TPMs(3, :), ... 'motion', 'on', ... 'nuisances', 'on', ... diff --git a/src/batches/setBatchImageCalculation.m b/src/batches/setBatchImageCalculation.m index e350d6b6..8dda3cb5 100644 --- a/src/batches/setBatchImageCalculation.m +++ b/src/batches/setBatchImageCalculation.m @@ -1,4 +1,4 @@ -function matlabbatch = setBatchImageCalculation(matlabbatch, input, output, outDir, expression) +function matlabbatch = setBatchImageCalculation(varargin) % % Set a batch for a image calculation % @@ -16,6 +16,22 @@ % :type outDir: string % :param expression: mathematical expression to apply (for example '(i1+i2)>3') % :type expression: string + % :param expression: data type that must be one of the following: + % - 'uint8' + % - 'int16' (default) + % - 'int32' + % - 'float32' + % - 'float64' + % - 'int8' + % - 'uint16' + % - 'uint32' + % :type expression: string + % + % See ``spm_cfg_imcalc.m`` for more information:: + % + % ``edit(fullfile(spm('dir'), 'config', 'spm_cfg_imcalc.m'))`` + % + % % % :returns: - :matlabbatch: % @@ -23,15 +39,47 @@ printBatchName('image calculation'); - matlabbatch{end + 1}.spm.util.imcalc.input = input; - matlabbatch{end}.spm.util.imcalc.output = output; - matlabbatch{end}.spm.util.imcalc.outdir = { outDir }; - matlabbatch{end}.spm.util.imcalc.expression = expression; + allowedDataType = {'uint8', ... + 'int16', ... + 'int32', ... + 'float32', ... + 'float64', ... + 'int8', ... + 'uint16', ... + 'uint32'}; + + defaultDataType = 'float32'; + + p = inputParser; + + addRequired(p, 'matlabbatch', @iscell); + addRequired(p, 'input'); + addRequired(p, 'output', @ischar); + addRequired(p, 'outDir', @ischar); + addRequired(p, 'expression', @ischar); + addOptional(p, 'dataType', defaultDataType, @ischar); + + parse(p, varargin{:}); + + if ~ismember(p.Results.dataType, allowedDataType) + fprintf(1, '\t%s\n', char(allowedDataType)); + errorStruct.identifier = [mfilename ':invalidDatatype']; + errorStruct.message = 'dataType must be one of the type mentionned above.'; + error(errorStruct); + end + + imcalc.input = p.Results.input; + imcalc.output = p.Results.output; + imcalc.outdir = { p.Results.outDir }; + imcalc.expression = p.Results.expression; + imcalc.options.dtype = spm_type(p.Results.dataType); + + % imcalc.var = struct('name', {}, 'value', {}); + % imcalc.options.dmtx = 0; + % imcalc.options.mask = 0; + % imcalc.options.interp = 1; - % matlabbatch{1}.spm.util.imcalc.var = struct('name', {}, 'value', {}); - % matlabbatch{1}.spm.util.imcalc.options.dmtx = 0; - % matlabbatch{1}.spm.util.imcalc.options.mask = 0; - % matlabbatch{1}.spm.util.imcalc.options.interp = 1; - % matlabbatch{1}.spm.util.imcalc.options.dtype = 4; + matlabbatch = p.Results.matlabbatch; + matlabbatch{end + 1}.spm.util.imcalc = imcalc; end diff --git a/src/batches/setBatchLesionAbnormalitiesDetection.m b/src/batches/setBatchLesionAbnormalitiesDetection.m index 8ea61493..3df7abd5 100755 --- a/src/batches/setBatchLesionAbnormalitiesDetection.m +++ b/src/batches/setBatchLesionAbnormalitiesDetection.m @@ -1,4 +1,4 @@ -function matlabbatch = setBatchLesionAbnormalitiesDetection(matlabbatch, BIDS, opt, subLabel) +function matlabbatch = setBatchLesionAbnormalitiesDetection(matlabbatch, opt, images) % % Creates a batch to detect lesion abnormalities % @@ -15,26 +15,17 @@ printBatchName('Lesion abnormalities'); - % Defin smoothed segmented images of patients - matlabbatch{1}.spm.tools.ali.outliers_detection.step3tissue.step3patients = ''; + outliers_detection = opt.toolbox.ALI.outliers_detection; - % Defin smoothed segmented images of neurotypical controls - matlabbatch{1}.spm.tools.ali.outliers_detection.step3tissue.step3controls = ''; + for i = 1:size(images, 1) - % Specify alpha parameter - matlabbatch{1}.spm.tools.ali.outliers_detection.step3tissue.step3Alpha = 0.5; - % Specify lambda parameter - matlabbatch{1}.spm.tools.ali.outliers_detection.step3tissue.step3Lambda = -4; + % 1. Define smoothed segmented tissue images of patients + outliers_detection.step3tissue(i).step3patients = cellstr(char(images(i, 1).patients)); - % define SPM folder - spmDir = spm('dir'); + % 2. Define smoothed segmented tissue images of neurotypical controls + outliers_detection.step3tissue(i).step3controls = cellstr(char(images(i, 1).controls)); - % specify lesion mask - lesionMask = fullfile(spmDir, 'toolbox', 'ALI', 'Mask_image', 'mask_controls_vox2mm.nii'); - - outliers_detection.step3mask_thr = 0; % threshold for the mask - outliers_detection.step3binary_thr = 0.3; % binary lesion: threshold U - outliers_detection.step3binary_size = 0.8; % binary lesion: minimum size (in cm3) + end matlabbatch{end + 1}.spm.tools.ali.outliers_detection = outliers_detection; diff --git a/src/batches/setBatchLesionOverlapMap.m b/src/batches/setBatchLesionOverlapMap.m index 3f7dfc37..ab08c8a6 100755 --- a/src/batches/setBatchLesionOverlapMap.m +++ b/src/batches/setBatchLesionOverlapMap.m @@ -17,3 +17,5 @@ % Specify lesion overlap map matlabbatch{1}.spm.tools.ali.lesion_overlap.lom = ''; + +end diff --git a/src/batches/setBatchLesionSegmentation.m b/src/batches/setBatchLesionSegmentation.m index 858cea1d..e8f6872b 100755 --- a/src/batches/setBatchLesionSegmentation.m +++ b/src/batches/setBatchLesionSegmentation.m @@ -15,26 +15,12 @@ printBatchName('Lesion segmentation'); + unified_segmentation = opt.toolbox.ALI.unified_segmentation; + % find anatomical file [anatImage, anatDataDir] = getAnatFilename(BIDS, subLabel, opt); unified_segmentation.step1data{1} = fullfile(anatDataDir, anatImage); - % define SPM folder - spmDir = spm('dir'); - - % specify Prior EXTRA class (lesion prior map) - lesionPriorMap = fullfile(spmDir, 'toolbox', 'ALI', 'Priors_extraClass', 'wc4prior0.nii'); - - unified_segmentation.step1prior = {lesionPriorMap}; - - unified_segmentation.step1niti = 2; % number of iterations - unified_segmentation.step1thr_prob = 0.333333333333333; % threshold probability - unified_segmentation.step1thr_size = 0.8; % threshold size (in cm3) - unified_segmentation.step1coregister = 1; % coregister in MNI space (yes: 1) - unified_segmentation.step1mask = {''}; % specify cost function mask(optional) - unified_segmentation.step1vox = 2; % Voxel sizes (in mm) - unified_segmentation.step1fwhm = [8 8 8]; % Smooth: FWHM - matlabbatch{end + 1}.spm.tools.ali.unified_segmentation = unified_segmentation; end diff --git a/src/batches/setBatchSTC.m b/src/batches/setBatchSTC.m index 462493a0..d71a5d55 100644 --- a/src/batches/setBatchSTC.m +++ b/src/batches/setBatchSTC.m @@ -1,10 +1,10 @@ -function matlabbatch = setBatchSTC(matlabbatch, BIDS, opt, subLabel) +function matlabbatch = setBatchSTC(varargin) % % Creates batch for slice timing correction % % USAGE:: % - % matlabbatch = setBatchSTC(matlabbatch, BIDS, opt, subID) + % matlabbatch = setBatchSTC(matlabbatch, BIDS, opt, subLabel) % % :param BIDS: BIDS layout returned by ``getData``. % :type BIDS: structure @@ -30,6 +30,24 @@ % % (C) Copyright 2019 CPP_SPM developers + p = inputParser; + + addRequired(p, 'matlabbatch', @iscell); + addRequired(p, 'BIDS', @isstruct); + addRequired(p, 'opt', @isstruct); + addRequired(p, 'subLabel', @ischar); + + parse(p, varargin{:}); + + matlabbatch = p.Results.matlabbatch; + BIDS = p.Results.BIDS; + opt = p.Results.opt; + subLabel = p.Results.subLabel; + + if opt.stc.skip + return + end + % get slice order sliceOrder = getSliceOrder(opt, 1); if isempty(sliceOrder) @@ -55,10 +73,10 @@ maxSliceTime = max(sliceOrder); minSliceTime = min(sliceOrder); - if isempty(opt.STC_referenceSlice) + if isempty(opt.stc.referenceSlice) referenceSlice = (maxSliceTime - minSliceTime) / 2; else - referenceSlice = opt.STC_referenceSlice; + referenceSlice = opt.stc.referenceSlice; end if TA >= TR || referenceSlice > TA || any(sliceOrder > TA) @@ -80,11 +98,11 @@ error(errorStruct); end - matlabbatch{end + 1}.spm.temporal.st.nslices = nbSlices; - matlabbatch{end}.spm.temporal.st.tr = TR; - matlabbatch{end}.spm.temporal.st.ta = TA; - matlabbatch{end}.spm.temporal.st.so = sliceOrder * 1000; - matlabbatch{end}.spm.temporal.st.refslice = referenceSlice * 1000; + temporal.st.nslices = nbSlices; + temporal.st.tr = TR; + temporal.st.ta = TA; + temporal.st.so = sliceOrder * 1000; + temporal.st.refslice = referenceSlice * 1000; [sessions, nbSessions] = getInfo(BIDS, subLabel, opt, 'Sessions'); @@ -106,7 +124,7 @@ file = validationInputFile(subFuncDataDir, fileName); % add the file to the list - matlabbatch{end}.spm.temporal.st.scans{runCounter} = {file}; + temporal.st.scans{runCounter} = {file}; runCounter = runCounter + 1; @@ -116,6 +134,8 @@ end + matlabbatch{end + 1}.spm.temporal = temporal; + % The following lines are commented out because those parameters % can be set in the spm_my_defaults.m % matlabbatch{1}.spm.temporal.st.prefix = spm_get_defaults('slicetiming.prefix'); diff --git a/src/batches/setBatchSkullStripping.m b/src/batches/setBatchSkullStripping.m index db416ea3..8383f8d1 100644 --- a/src/batches/setBatchSkullStripping.m +++ b/src/batches/setBatchSkullStripping.m @@ -36,8 +36,16 @@ printBatchName('skull stripping'); - [anatImage, anatDataDir] = getAnatFilename(BIDS, subLabel, opt); - output = ['m' strrep(anatImage, '.nii', '_skullstripped.nii')]; + [imageToSkullStrip, dataDir] = getAnatFilename(BIDS, subLabel, opt); + + % if the input image is mean func image instead of anatomical + if opt.skullstrip.mean + [imageToSkullStrip, dataDir] = getMeanFuncFilename(BIDS, subLabel, opt); + end + + output = ['m' strrep(imageToSkullStrip, '.nii', '_skullstripped.nii')]; + maskOutput = ['m' strrep(imageToSkullStrip, '.nii', '_mask.nii')]; + expression = sprintf('i1.*((i2+i3+i4)>%f)', opt.skullstrip.threshold); % if this is part of a pipeline we get the segmentation dependency to get @@ -106,13 +114,13 @@ end - matlabbatch = setBatchImageCalculation(matlabbatch, input, output, anatDataDir, expression); + matlabbatch = setBatchImageCalculation(matlabbatch, input, output, dataDir, expression); %% Add a batch to output the mask matlabbatch{end + 1} = matlabbatch{end}; matlabbatch{end}.spm.util.imcalc.expression = sprintf( ... '(i2+i3+i4)>%f', ... opt.skullstrip.threshold); - matlabbatch{end}.spm.util.imcalc.output = ['m' strrep(anatImage, '.nii', '_mask.nii')]; + matlabbatch{end}.spm.util.imcalc.output = maskOutput; end diff --git a/src/batches/setBatchSubjectLevelGLMSpec.m b/src/batches/setBatchSubjectLevelGLMSpec.m index 70fd2795..fbfd0452 100644 --- a/src/batches/setBatchSubjectLevelGLMSpec.m +++ b/src/batches/setBatchSubjectLevelGLMSpec.m @@ -6,15 +6,18 @@ % % matlabbatch = setBatchSubjectLevelGLMSpec(matlabbatch, BIDS, opt, subLabel, funcFWHM) % - % :param argin1: (dimension) obligatory argument. Lorem ipsum dolor sit amet, - % consectetur adipiscing elit. Ut congue nec est ac lacinia. - % :type argin1: type - % :param argin2: Options chosen for the analysis. See ``checkOptions()``. - % :type argin2: string - % :param argin3: (dimension) optional argument + % :param matlabbatch: + % :type matlabbatch: structure + % :param BIDS: + % :type BIDS: structure + % :param opt: + % :type opt: structure + % :param subLabel: + % :type subLabel: string + % :param funcFWHM: + % :type funcFWHM: float % - % :returns: - :argout1: (type) (dimension) - % - :argout2: (type) (dimension) + % :returns: - :argout1: (structure) (matlabbatch) % % (C) Copyright 2019 CPP_SPM developers @@ -54,10 +57,10 @@ % If no reference slice is given for STC, then STC took the mid-volume % time point to do the correction. % When no STC was done, this is usually a good way to do it too. - if isempty(opt.STC_referenceSlice) + if isempty(opt.stc.referenceSlice) refBin = floor(nbTimeBins / 2); else - refBin = opt.STC_referenceSlice / opt.metadata.RepetitionTime; + refBin = opt.stc.referenceSlice / opt.metadata.RepetitionTime; end matlabbatch{end}.spm.stats.fmri_spec.timing.fmri_t0 = refBin; diff --git a/src/defaults/ALI_my_defaults.m b/src/defaults/ALI_my_defaults.m new file mode 100755 index 00000000..3cb7628b --- /dev/null +++ b/src/defaults/ALI_my_defaults.m @@ -0,0 +1,65 @@ +function defaults = ALI_my_defaults() + % + % Short description of what the function does goes here. + % + % USAGE:: + % + % defaults = ALI_my_defaults() + % + % This is where we set the defautls we want to use for the ALE (lesion) toolbox. + % These will overide the spm defaults. + % When "not enough" information is specified in the batch files, SPM falls + % back on the defaults to fill in the blanks. This allows to make the + % script simpler. + % + % (C) Copyright 2021 CPP_SPM developers + + % Lesion segmentation defaults + % ========================================================================== + + % define SPM folder + spmDir = spm('dir'); + + % specify Prior EXTRA class (lesion prior map) + lesionPriorMap = fullfile(spmDir, 'toolbox', 'ALI', ... + 'Priors_extraClass', 'wc4prior0.nii'); + + defaults.toolbox.ALI.unified_segmentation.step1prior = {lesionPriorMap}; + + % number of iterations + defaults.toolbox.ALI.unified_segmentation.step1niti = 2; + % threshold probability + defaults.toolbox.ALI.unified_segmentation.step1thr_prob = 0.333; + + % threshold size (in cm3) + defaults.toolbox.ALI.unified_segmentation.step1thr_size = 0.8; + % coregister in MNI space (yes: 1) + defaults.toolbox.ALI.unified_segmentation.step1coregister = 1; + % specify cost function mask(optional) + defaults.toolbox.ALI.unified_segmentation.step1mask = {''}; + % Voxel sizes (in mm) + defaults.toolbox.ALI.unified_segmentation.step1vox = 2; + % Smooth: FWHM + defaults.toolbox.ALI.unified_segmentation.step1fwhm = [8 8 8]; + + % Lesion abnormalities detection defaults + % ========================================================================== + + % Specify alpha parameter + defaults.toolbox.ALI.outliers_detection.step3tissue.step3Alpha = 0.5; + + % Specify lambda parameter + defaults.toolbox.ALI.outliers_detection.step3tissue.step3Lambda = -4; + + % specify lesion mask + defaults.toolbox.ALI.lesionMask = fullfile(spmDir, 'toolbox', 'ALI', ... + 'Mask_image', 'mask_controls_vox2mm.nii'); + + % threshold for the mask + defaults.toolbox.ALI.outliers_detection.step3mask_thr = 0; + % binary lesion: threshold U + defaults.toolbox.ALI.outliers_detection.step3binary_thr = 0.3; + % binary lesion: minimum size (in cm3) + defaults.toolbox.ALI.outliers_detection.step3binary_size = 0.8; + +end diff --git a/src/defaults/checkOptions.m b/src/defaults/checkOptions.m index 878184df..4ac1aefc 100644 --- a/src/defaults/checkOptions.m +++ b/src/defaults/checkOptions.m @@ -50,13 +50,24 @@ % will be included in the mask. % - ``opt.funcVoxelDims = []`` - Voxel dimensions to use for resampling of functional data % at normalization. - % - ``opt.STC_referenceSlice = []`` - reference slice for the slice timing correction. + % - ``opt.stc.skip = false`` - boolean flag to skip slice time correction or not. + % - ``opt.stc.referenceSlice = []`` - reference slice for the slice timing correction. % If left emtpy the mid-volume acquisition time point will be selected at run time. - % - ``opt.sliceOrder = []`` - To be used if SPM can't extract slice info. NOT RECOMMENDED, + % - ``opt.stc.sliceOrder = []`` - To be used if SPM can't extract slice info. NOT RECOMMENDED, % if you know the order in which slices were acquired, you should be able to recompute % slice timing and add it to the json files in your BIDS data set. % - ``opt.glm.roibased.do`` - % - ``opt.glm.QA.do = true`` - If set to ``true`` the residual images of a + % - ``opt.QA.func.carpetPlot = true`` to plot carpet plot when running ``functionaQA`` + % - ``opt.QA.func`` contains a lot of options used by ``spmup_first_level_qa`` + % in ``functionaQA`` + % - ``opt.QA.func.MotionParameters = 'on'`` + % - ``opt.QA.func.FramewiseDisplacement = 'on'`` + % - ``opt.QA.func.Voltera = 'on'`` + % - ``opt.QA.func.Globals = 'on'`` + % - ``opt.QA.func.Movie = 'on'`` ; set it to ``off`` to skip generating movies + % of the time series + % - ``opt.QA.func.Basics = 'on'`` + % - ``opt.QA.glm.do = true`` - If set to ``true`` the residual images of a % GLM at the subject levels will be used to estimate if there is any remaining structure % in the GLM residuals (the power spectra are not flat) that could indicate % the subject level results are likely confounded (see @@ -69,9 +80,18 @@ % (C) Copyright 2019 CPP_SPM developers fieldsToSet = setDefaultOption(); + opt = setFields(opt, fieldsToSet); opt = setFields(opt, fieldsToSet); + % Options for toolboxes + global ALI_TOOLBOX_PRESENT + + checkToolbox('ALI'); + if ALI_TOOLBOX_PRESENT + opt = setFields(opt, ALI_my_defaults()); + end + checkFields(opt); if ~isempty(opt.dataDir) @@ -103,8 +123,9 @@ %% Options for slice time correction % all in seconds - fieldsToSet.STC_referenceSlice = []; - fieldsToSet.sliceOrder = []; + fieldsToSet.stc.referenceSlice = []; + fieldsToSet.stc.sliceOrder = []; + fieldsToSet.stc.skip = false; %% Options for realign fieldsToSet.realign.useUnwarp = true; @@ -112,6 +133,7 @@ %% Options for segmentation fieldsToSet.skullstrip.threshold = 0.75; + fieldsToSet.skullstrip.mean = false; %% Options for normalize fieldsToSet.space = 'MNI'; @@ -122,7 +144,15 @@ fieldsToSet.model.hrfDerivatives = [0 0]; fieldsToSet.contrastList = {}; - fieldsToSet.glm.QA.do = true; + fieldsToSet.QA.glm.do = true; + fieldsToSet.QA.func.carpetPlot = true; + fieldsToSet.QA.func.Motion = 'on'; + fieldsToSet.QA.func.FD = 'on'; + fieldsToSet.QA.func.Voltera = 'on'; + fieldsToSet.QA.func.Globals = 'on'; + fieldsToSet.QA.func.Movie = 'on'; + fieldsToSet.QA.func.Basics = 'on'; + fieldsToSet.glm.roibased.do = false; % specify the results to compute @@ -130,7 +160,7 @@ fieldsToSet.parallelize.do = false; fieldsToSet.parallelize.nbWorkers = 3; - fieldsToSet.parallelize.killOnExit = true; + fieldsToSet.parallelize.killOnExit = false; end @@ -163,13 +193,13 @@ function checkFields(opt) end - if ~isempty (opt.STC_referenceSlice) && length(opt.STC_referenceSlice) > 1 + if ~isempty (opt.stc.referenceSlice) && length(opt.stc.referenceSlice) > 1 errorStruct.identifier = 'checkOptions:refSliceNotScalar'; errorStruct.message = sprintf( ... - ['options.STC_referenceSlice should be a scalar.' ... + ['options.stc.referenceSlice should be a scalar.' ... '\nCurrent value is: %d'], ... - opt.STC_referenceSlice); + opt.stc.referenceSlice); error(errorStruct); end diff --git a/src/defaults/miss_hit.cfg b/src/defaults/miss_hit.cfg index a9f91c98..b09b1d42 100644 --- a/src/defaults/miss_hit.cfg +++ b/src/defaults/miss_hit.cfg @@ -1 +1 @@ -regex_function_name: "[a-z]+(_*([a-zA-Z0-9]){1}[A-Za-z]+)*" # almost anything goes in the root folder +regex_function_name: "[a-zA-Z]+(_*([a-zA-Z0-9]){1}[A-Za-z]+)*" # almost anything goes in the root folder diff --git a/src/getPrefix.m b/src/getPrefix.m index ef334d9b..724ea992 100644 --- a/src/getPrefix.m +++ b/src/getPrefix.m @@ -56,6 +56,10 @@ prefix = ['mean' prefix]; + if strcmp(opt.space, 'MNI') + prefix = ['w', prefix]; + end + case 'funcqa' prefix = getPrefix('realign', opt); @@ -105,9 +109,10 @@ function prefix = prefixForSTC(prefix, opt) % Check the slice timing information is not in the metadata and not added % manually in the opt variable. - if (isfield(opt.metadata, 'SliceTiming') && ... - ~isempty(opt.metadata.SliceTiming)) || ... - ~isempty(opt.sliceOrder) + if ~opt.stc.skip && ... + ((isfield(opt.metadata, 'SliceTiming') && ... + ~isempty(opt.metadata.SliceTiming)) || ... + ~isempty(opt.stc.sliceOrder)) prefix = [spm_get_defaults('slicetiming.prefix') prefix]; end end diff --git a/src/getSliceOrder.m b/src/getSliceOrder.m index 1d2e1037..d4aa73d1 100644 --- a/src/getSliceOrder.m +++ b/src/getSliceOrder.m @@ -41,8 +41,8 @@ % If slice timing information is not in the metadata, you have the option % to add the slice order manually in the "opt" in the "getOptions" % function - if ~isempty(opt.sliceOrder) - sliceOrder = opt.sliceOrder; + if ~isempty(opt.stc.sliceOrder) + sliceOrder = opt.stc.sliceOrder; msg{end + 1} = ' SLICE TIMING INFORMATION EXTRACTED FROM OPTIONS.\n\n'; diff --git a/src/subject_level/convertOnsetTsvToMat.m b/src/subject_level/convertOnsetTsvToMat.m index 3076428e..b4584931 100644 --- a/src/subject_level/convertOnsetTsvToMat.m +++ b/src/subject_level/convertOnsetTsvToMat.m @@ -1,9 +1,12 @@ function fullpathOnsetFileName = convertOnsetTsvToMat(opt, tsvFile) % - % Converts an events.tsv file to an onset file suitable for SPM subject level + % Converts an events.tsv file to an onset file suitable for SPM subject level % analysis. - % The scripts extracts the trial type, onsets, and durations, and - % converts them and stores them in a mat file. + % + % The function extracts from the events.tsv file the trials (with type, onsets, and durations) + % of th conditions of interest as requested in the model.json. + % It then stores them in a .mat file that can be fed directly in an SPM GLM + % batch. % % USAGE:: % diff --git a/src/subject_level/createAndReturnOnsetFile.m b/src/subject_level/createAndReturnOnsetFile.m index 92a22ad6..0f2aecfd 100644 --- a/src/subject_level/createAndReturnOnsetFile.m +++ b/src/subject_level/createAndReturnOnsetFile.m @@ -1,8 +1,12 @@ function onsetFileName = createAndReturnOnsetFile(opt, subID, tsvFile, funcFWHM) % - % For a given events.tsv file it creates a .mat file that can directly be used - % for the GLM specification of a subject level model. The file is moved - % directly into the folder of the GLM. + % Creates an ``_onset.mat`` in the subject level GLM folder. + % + % For a given ``_events.tsv`` file and ``_model.json``, + % it creates a ``_onset.mat`` file that can directly be used + % for the GLM specification of a subject level model. + % + % The file is moved directly into the folder of the GLM. % % USAGE:: % diff --git a/src/templates/bidsTemplateWorkflow.m b/src/templates/bidsTemplateWorkflow.m index 2294c51b..626a5f48 100644 --- a/src/templates/bidsTemplateWorkflow.m +++ b/src/templates/bidsTemplateWorkflow.m @@ -11,9 +11,9 @@ function bidsTemplateWorkflow(opt) printProcessingSubject(iSub, subLabel); - matlabbatch = []; + matlabbatch = {}; - % matlabbatch = setBatchSTC(matlabbatch, BIDS, opt, subLabel); + matlabbatch = setBatchSomeBatch(matlabbatch, BIDS, opt, subLabel); saveAndRunWorkflow(matlabbatch, 'workflow name', opt, subLabel); diff --git a/src/templates/setBatchTemplate.m b/src/templates/setBatchTemplate.m index f17b0d03..210aa23b 100644 --- a/src/templates/setBatchTemplate.m +++ b/src/templates/setBatchTemplate.m @@ -1,10 +1,10 @@ -function matlabbatch = setBatchTemplate(matlabbatch, BIDS, opt, subID, info, varargin) +function matlabbatch = setBatchTemplate(matlabbatch, BIDS, opt, subID) % % template to creae new setBatch functions % % USAGE:: % - % matlabbatch = setBatchTemplate(matlabbatch, BIDS, opt, subID, info, varargin) + % matlabbatch = setBatchTemplate(matlabbatch, BIDS, opt, subID) % % :param matlabbatch: % :type matlabbatch: @@ -18,6 +18,5 @@ matlabbatch{end + 1}.spm.something = BIDS; matlabbatch{end}.spm.else = opt; matlabbatch{end}.spm.other = subID; - matlabbatch{end}.spm.thing = info; end diff --git a/src/utils/checkToolbox.m b/src/utils/checkToolbox.m new file mode 100644 index 00000000..83c20ffe --- /dev/null +++ b/src/utils/checkToolbox.m @@ -0,0 +1,24 @@ +function checkToolbox(toolboxName) + % + % Checks that that the right dependencies are installeda and + % loads the spm defaults. + % + % USAGE:: + % + % checkToolbox() + % + % + % (C) Copyright 2021 CPP_SPM developers + + global ALI_TOOLBOX_PRESENT + + ALI_TOOLBOX_PRESENT = false; + + if strcmp(toolboxName, 'ALI') + % ALI toolbox + if exist(fullfile(spm('dir'), 'toolbox', 'ALI'), 'dir') + ALI_TOOLBOX_PRESENT = true; + end + end + +end diff --git a/src/utils/manageWorkersPool.m b/src/utils/manageWorkersPool.m index 76a1f3e6..7a91ef35 100644 --- a/src/utils/manageWorkersPool.m +++ b/src/utils/manageWorkersPool.m @@ -19,61 +19,61 @@ function manageWorkersPool(action, opt) % % (C) Copyright 2020 CPP_SPM developers + if isOctave() || ~license('test', 'Distrib_Computing_Toolbox') + return + end + if ~opt.parallelize.do opt.parallelize.nbWorkers = 1; - opt.parallelize.killOnExit = true; + opt.parallelize.killOnExit = false; end - if ~isOctave() - - matlabVer = version('-release'); - - nbWorkers = opt.parallelize.nbWorkers; + matlabVer = version('-release'); - switch lower(action) + nbWorkers = opt.parallelize.nbWorkers; - case 'open' + switch lower(action) - if str2double(matlabVer(1:4)) > 2013 + case 'open' - pool = gcp('nocreate'); + if str2double(matlabVer(1:4)) > 2013 - if isempty(pool) - parpool(nbWorkers); %#ok<*DPOOL> - end + pool = gcp('nocreate'); - else + if isempty(pool) + parpool(nbWorkers); %#ok<*DPOOL> + end - if matlabpool('size') == 0 %#ok<*DPOOL> - matlabpool(nbWorkers); + else - elseif matlabpool('size') ~= nbWorkers - matlabpool close; - matlabpool(nbWorkers); + if matlabpool('size') == 0 %#ok<*DPOOL> + matlabpool(nbWorkers); - end + elseif matlabpool('size') ~= nbWorkers + matlabpool close; + matlabpool(nbWorkers); end - case 'close' + end - if opt.parallelize.killOnExit + case 'close' - if str2double(matlabVer(1:4)) > 2013 + if opt.parallelize.killOnExit - pool = gcp('nocreate'); - if ~isempty(pool) - delete(gcp); - end - - else - matlabpool close; + if str2double(matlabVer(1:4)) > 2013 + pool = gcp('nocreate'); + if ~isempty(pool) + delete(gcp); end + else + matlabpool close; + end - end + end end diff --git a/src/workflows/bidsConcatBetaTmaps.m b/src/workflows/bidsConcatBetaTmaps.m index ed11cc87..44c733bf 100644 --- a/src/workflows/bidsConcatBetaTmaps.m +++ b/src/workflows/bidsConcatBetaTmaps.m @@ -13,9 +13,15 @@ function bidsConcatBetaTmaps(opt, funcFWHM, deleteIndBeta, deleteIndTmaps) % :param deleteIndTmaps: decide to delete t-maps % :type funcFWHM: (boolean) % + % When concatenating betamaps: + % + % Ensures that there is only 1 image per "contrast". + % Creates a tsv that lists the content of the 4D image. + % This TSV is in the subject level GLM folder where the beta map came from. + % This TSV file is named ``sub-subLabel_task-taskName_space-space_labelfold.tsv``. + % % (C) Copyright 2019 CPP_SPM developers - % delete individual Beta and tmaps if nargin < 3 deleteIndBeta = 1; deleteIndTmaps = 1; @@ -33,6 +39,8 @@ function bidsConcatBetaTmaps(opt, funcFWHM, deleteIndBeta, deleteIndTmaps) ffxDir = getFFXdir(subLabel, funcFWHM, opt); + load(fullfile(ffxDir, 'SPM.mat')); + contrasts = specifyContrasts(ffxDir, opt.taskName, opt); beta_maps = cell(length(contrasts), 1); @@ -40,8 +48,25 @@ function bidsConcatBetaTmaps(opt, funcFWHM, deleteIndBeta, deleteIndTmaps) % path to beta and t-map files. for iContrast = 1:length(beta_maps) - % Note that the betas are created from the idx (Beta_idx(iBeta)) - fileName = sprintf('beta_%04d.nii', find(contrasts(iContrast).C)); + + betasIndices = find(contrasts(iContrast).C); + + if numel(betasIndices) > 1 + error('Supposed to concatenate one beta image per contrast.'); + end + + % for this beta iamge we identify + % - which run it came from + % - the exact condition name stored in the SPM.mat + % so they can be saved in a tsv for for "label" and "fold" for MVPA + tmp = cat(1, SPM.Sess(:).col) == betasIndices; + runs(iContrast, 1) = find(any(tmp, 2)); + + tmp = SPM.xX.name{betasIndices}; + parts = strsplit(tmp, ' '); + conditions{iContrast, 1} = strjoin(parts(2:end), ' '); + + fileName = sprintf('beta_%04d.nii', betasIndices); fileName = validationInputFile(ffxDir, fileName); beta_maps{iContrast, 1} = [fileName, ',1']; @@ -49,12 +74,26 @@ function bidsConcatBetaTmaps(opt, funcFWHM, deleteIndBeta, deleteIndTmaps) fileName = sprintf('spmT_%04d.nii', iContrast); fileName = validationInputFile(ffxDir, fileName); t_maps{iContrast, 1} = [fileName, ',1']; + end + % tsv + nameStructure = struct( ... + 'ext', '.tsv', ... + 'type', 'labelfold', ... + 'sub', subLabel, ... + 'task', opt.taskName, ... + 'space', opt.space); + tsvName = createFilename(nameStructure); + + tsvContent = struct('folds', runs, 'labels', {conditions}); + + spm_save(fullfile(ffxDir, tsvName), tsvContent); + % beta maps outputName = ['4D_beta_', num2str(funcFWHM), '.nii']; - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatch3Dto4D(matlabbatch, beta_maps, RT, outputName); % t-maps diff --git a/src/workflows/bidsCopyRawFolder.m b/src/workflows/bidsCopyRawFolder.m index 230eeb98..d8425cd7 100644 --- a/src/workflows/bidsCopyRawFolder.m +++ b/src/workflows/bidsCopyRawFolder.m @@ -177,7 +177,7 @@ function copyModalities(BIDS, opt, modalities, subLabel, session) subDir, ... sessionDir); - [~, ~, ~] = mkdir(fullfile(targetFolder, modalities{iModality})); + spm_mkdir(targetFolder, modalities{iModality}); srcFolder = fullfile(rawDir, ... subDir, ... @@ -193,12 +193,12 @@ function copyModalities(BIDS, opt, modalities, subLabel, session) 'task', opt.taskName); for iFile = 1:size(files, 1) - copyToDerivative(files{iFile}, fullfile(targetFolder, 'func')); + copyToDerivative(files{iFile}, fullfile(targetFolder, modalities{iModality})); p = bids.internal.parse_filename(files{iFile}); sidecar = strrep(p.filename, p.ext, '.json'); if exist(fullfile(fileparts(files{iFile}), sidecar), 'file') copyToDerivative(fullfile(fileparts(files{iFile}), sidecar), ... - fullfile(targetFolder, 'func')); + fullfile(targetFolder, modalities{iModality})); end end @@ -253,7 +253,7 @@ function unzipFiles(derivativesDir, deleteZippedNii, opt) % for bold, physio and stim files, we only unzip the files of the task of % interest if any(strcmp(fragments.type, {'bold', 'stim'})) && ... - isfield(fragments, 'task') && strcmp(fragments.task, opt.taskName) + isfield(fragments, 'task') && strcmp(fragments.task, opt.taskName) %#ok % load the nifti image and saves the functional data as unzipped nii n = load_untouch_nii(file); diff --git a/src/workflows/bidsCreateVDM.m b/src/workflows/bidsCreateVDM.m index c235682c..4f18a3f6 100644 --- a/src/workflows/bidsCreateVDM.m +++ b/src/workflows/bidsCreateVDM.m @@ -40,11 +40,11 @@ function bidsCreateVDM(opt) [fileName, subFuncDataDir] = getBoldFilename(BIDS, subLabel, sessions{1}, runs{1}, opt); spmup_basics(fullfile(subFuncDataDir, fileName), 'mean'); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchCoregistrationFmap(matlabbatch, BIDS, opt, subLabel); saveAndRunWorkflow(matlabbatch, 'coregister_fmap', opt, subLabel); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchCreateVDMs(matlabbatch, BIDS, opt, subLabel); saveAndRunWorkflow(matlabbatch, 'create_vdm', opt, subLabel); diff --git a/src/workflows/bidsFFX.m b/src/workflows/bidsFFX.m index ceee4257..f30b09ae 100644 --- a/src/workflows/bidsFFX.m +++ b/src/workflows/bidsFFX.m @@ -49,7 +49,7 @@ function bidsFFX(action, opt, funcFWHM) printProcessingSubject(iSub, subLabel); - matlabbatch = []; + matlabbatch = {}; switch action @@ -85,7 +85,7 @@ function bidsFFX(action, opt, funcFWHM) saveAndRunWorkflow(matlabbatch, batchName, opt, subLabel); - if opt.glm.QA.do + if opt.QA.glm.do plot_power_spectra_of_GLM_residuals( ... getFFXdir(subLabel, funcFWHM, opt), ... opt.metadata.RepetitionTime); diff --git a/src/workflows/bidsLesionAbnormalitiesDetection.m b/src/workflows/bidsLesionAbnormalitiesDetection.m new file mode 100755 index 00000000..198c1ea3 --- /dev/null +++ b/src/workflows/bidsLesionAbnormalitiesDetection.m @@ -0,0 +1,58 @@ +function bidsLesionAbnormalitiesDetection(opt) + % + % Step 2. Detects lesion abnormalities in anatomical image after segmentation of the image. + % + % USAGE:: + % + % bidsLesionAbnormalitiesDetection(opt) + % + % :param opt: structure or json filename containing the options. See + % ``checkOptions()`` and ``loadAndCheckOptions()``. + % :type opt: structure + % + % Lesion abnormalities detection will be performed using the information provided + % from the lesion segmentation output in BIDS format. + % + % (C) Copyright 2021 CPP_SPM developers + + [BIDS, opt] = setUpWorkflow(opt, 'abnormalities detection'); + + prefixList = {'rc1', 'rc2'}; + + % create a structure to collect image names + for iPrefix = 1:numel(prefixList) + images(iPrefix, 1) = struct('controls', [], 'patients', []); + end + + for iSub = 1:numel(opt.subjects) + + subLabel = opt.subjects{iSub}; + + printProcessingSubject(iSub, subLabel); + + idx = strcmp(BIDS.participants.participant_id, ['sub-' subLabel]); + + temp = BIDS.participants.group(idx); + + [anatImage, anatDataDir] = getAnatFilename(BIDS, subLabel, opt); + + for iPrefix = 1:numel(prefixList) + + prefix = prefixList{iPrefix}; + files = validationInputFile(anatDataDir, anatImage, prefix); + + if strcmp (temp, 'control') + images(iPrefix, 1).controls{end + 1, 1} = files; + else + images(iPrefix, 1).patients{end + 1, 1} = files; + end + + end + end + + matlabbatch = {}; + matlabbatch = setBatchLesionAbnormalitiesDetection(matlabbatch, opt, images); + + saveAndRunWorkflow(matlabbatch, 'LesionAbnormalitiesDetection', opt, subLabel); + +end diff --git a/src/workflows/bidsLesionOverlapMap.m b/src/workflows/bidsLesionOverlapMap.m new file mode 100755 index 00000000..2a762445 --- /dev/null +++ b/src/workflows/bidsLesionOverlapMap.m @@ -0,0 +1,36 @@ +function bidsLesionOverlapMap(opt) + % + % Step 3. Creates lesion overlap map on the anatomical image after initial segmentation + % and lesion abnormality detection of the image. + % + % USAGE:: + % + % bidsLesionOverlapMap(opt) + % + % :param opt: structure or json filename containing the options. See + % ``checkOptions()`` and ``loadAndCheckOptions()``. + % :type opt: structure + % + % Lesion overlap map will be created using the information provided from the + % Lesion abnormalities detection output in BIDS format. + % + % (C) Copyright 2021 CPP_SPM developers + + [BIDS, opt] = setUpWorkflow(opt, 'lesion overlap map'); + + for iSub = 1:numel(opt.subjects) + + subLabel = opt.subjects{iSub}; + + printProcessingSubject(iSub, subLabel); + + matlabbatch = {}; + matlabbatch = setBatchLesionOverlapMap(matlabbatch, BIDS, opt, subLabel); + + saveAndRunWorkflow(matlabbatch, 'LesionOverlapMap', opt, subLabel); + + copyFigures(BIDS, opt, subLabel); + + end + +end diff --git a/src/workflows/bidsLesionSegmentation.m b/src/workflows/bidsLesionSegmentation.m index af0fb8da..27ca6cb8 100755 --- a/src/workflows/bidsLesionSegmentation.m +++ b/src/workflows/bidsLesionSegmentation.m @@ -22,12 +22,12 @@ function bidsLesionSegmentation(opt) printProcessingSubject(iSub, subLabel); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchLesionSegmentation(matlabbatch, BIDS, opt, subLabel); saveAndRunWorkflow(matlabbatch, 'LesionSegmentation', opt, subLabel); - copyFigures(BIDS, opt, subLabel); + % copyFigures(BIDS, opt, subLabel); end diff --git a/src/workflows/bidsRFX.m b/src/workflows/bidsRFX.m index fcb7e5cc..1dbc0e2c 100644 --- a/src/workflows/bidsRFX.m +++ b/src/workflows/bidsRFX.m @@ -46,7 +46,7 @@ function bidsRFX(action, opt, funcFWHM, conFWHM) case 'smoothContrasts' - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSmoothConImages(matlabbatch, opt, funcFWHM, conFWHM); saveAndRunWorkflow(matlabbatch, ... @@ -65,7 +65,7 @@ function bidsRFX(action, opt, funcFWHM, conFWHM) % - needs to be improved (maybe??) as the structural and mask may vary for % different analysis % ------ - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchMeanAnatAndMask(matlabbatch, ... opt, ... funcFWHM, ... @@ -74,7 +74,7 @@ function bidsRFX(action, opt, funcFWHM, conFWHM) % TODO % saving needs to be improved (maybe??) as the name may vary with FXHM and contrast - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchFactorialDesign(matlabbatch, opt, funcFWHM, conFWHM); % Load the list of contrasts of interest for the RFX @@ -86,7 +86,7 @@ function bidsRFX(action, opt, funcFWHM, conFWHM) % TODO % saving needs to be improved (maybe??) as the name may vary with FXHM and contrast rfxDir = getRFXdir(opt, funcFWHM, conFWHM); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchGroupLevelContrasts(matlabbatch, grpLvlCon, rfxDir); saveAndRunWorkflow(matlabbatch, 'contrasts_rfx', opt); diff --git a/src/workflows/bidsRealignReslice.m b/src/workflows/bidsRealignReslice.m index 0b90bb8d..631411ac 100644 --- a/src/workflows/bidsRealignReslice.m +++ b/src/workflows/bidsRealignReslice.m @@ -22,7 +22,7 @@ function bidsRealignReslice(opt) printProcessingSubject(iSub, subLabel); - matlabbatch = []; + matlabbatch = {}; [matlabbatch, ~] = setBatchRealign( ... matlabbatch, ... BIDS, ... diff --git a/src/workflows/bidsRealignUnwarp.m b/src/workflows/bidsRealignUnwarp.m index c56387a6..f90fbcd8 100644 --- a/src/workflows/bidsRealignUnwarp.m +++ b/src/workflows/bidsRealignUnwarp.m @@ -25,7 +25,7 @@ function bidsRealignUnwarp(opt) printProcessingSubject(iSub, subLabel); - matlabbatch = []; + matlabbatch = {}; [matlabbatch, ~] = setBatchRealign( ... matlabbatch, ... BIDS, ... diff --git a/src/workflows/bidsResliceTpmToFunc.m b/src/workflows/bidsResliceTpmToFunc.m index 3394c54f..6d85c181 100644 --- a/src/workflows/bidsResliceTpmToFunc.m +++ b/src/workflows/bidsResliceTpmToFunc.m @@ -34,7 +34,7 @@ function bidsResliceTpmToFunc(opt) [anatImage, anatDataDir] = getAnatFilename(BIDS, subLabel, opt); TPMs = validationInputFile(anatDataDir, anatImage, 'c[123]'); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchReslice(matlabbatch, ... fullfile(meanFuncDir, meanImage), ... cellstr(TPMs)); @@ -54,7 +54,7 @@ function bidsResliceTpmToFunc(opt) expression = sprintf('(i1+i2+i3)>%f', opt.skullstrip.threshold); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchImageCalculation(matlabbatch, input, output, meanFuncDir, expression); saveAndRunWorkflow(matlabbatch, 'create_functional_brain_mask', opt, subLabel); diff --git a/src/workflows/bidsResults.m b/src/workflows/bidsResults.m index ae744de4..e8c79095 100644 --- a/src/workflows/bidsResults.m +++ b/src/workflows/bidsResults.m @@ -18,17 +18,24 @@ function bidsResults(opt, funcFWHM, conFWHM) % images (Gaussian kernel size). % :type conFWHM: scalar % + % + % TODO + % + % move ps file + % rename NIDM file + % if it does not exist create the default "result" field from the BIDS model file + % + % % (C) Copyright 2020 CPP_SPM developers + currentDirectory = pwd; + [BIDS, opt] = setUpWorkflow(opt, 'computing GLM results'); if isempty(opt.model.file) opt = createDefaultModel(BIDS, opt); end - % TOD0 - % if it does not exist create the default "result" field from the BIDS model file - % loop trough the steps and more results to compute for each contrast % mentioned for each step for iStep = 1:length(opt.result.Steps) @@ -40,7 +47,7 @@ function bidsResults(opt, funcFWHM, conFWHM) case 'run' warning('run level not implemented yet'); - matlabbatch = []; + % matlabbatch = {}; % saveMatlabBatch(matlabbatch, 'computeFfxResults', opt, subID); case 'subject' @@ -48,7 +55,7 @@ function bidsResults(opt, funcFWHM, conFWHM) % For each subject for iSub = 1:numel(opt.subjects) - matlabbatch = []; + matlabbatch = {}; subLabel = opt.subjects{iSub}; @@ -79,7 +86,7 @@ function bidsResults(opt, funcFWHM, conFWHM) case 'dataset' - matlabbatch = []; + matlabbatch = {}; results.dir = getRFXdir(opt, funcFWHM, conFWHM); results.contrastNb = 1; @@ -106,11 +113,7 @@ function bidsResults(opt, funcFWHM, conFWHM) end - % move ps file - % TODO - - % rename NIDM file - % TODO + cd(currentDirectory); end diff --git a/src/workflows/bidsRoiBasedGLM.m b/src/workflows/bidsRoiBasedGLM.m index 948a5a96..2e07f05d 100644 --- a/src/workflows/bidsRoiBasedGLM.m +++ b/src/workflows/bidsRoiBasedGLM.m @@ -32,7 +32,7 @@ function bidsRoiBasedGLM(opt) printProcessingSubject(iSub, subLabel); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSubjectLevelGLMSpec(matlabbatch, BIDS, opt, subLabel, funcFWHM); diff --git a/src/workflows/bidsSTC.m b/src/workflows/bidsSTC.m index 76c19eb5..33f94533 100644 --- a/src/workflows/bidsSTC.m +++ b/src/workflows/bidsSTC.m @@ -37,7 +37,7 @@ function bidsSTC(opt) printProcessingSubject(iSub, subLabel); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSTC(matlabbatch, BIDS, opt, subLabel); saveAndRunWorkflow(matlabbatch, 'STC', opt, subLabel); diff --git a/src/workflows/bidsSegmentSkullStrip.m b/src/workflows/bidsSegmentSkullStrip.m index 978e2b0c..826cb3ea 100644 --- a/src/workflows/bidsSegmentSkullStrip.m +++ b/src/workflows/bidsSegmentSkullStrip.m @@ -23,7 +23,7 @@ function bidsSegmentSkullStrip(opt) printProcessingSubject(iSub, subLabel); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSelectAnat(matlabbatch, BIDS, opt, subLabel); % dependency from file selector ('Anatomical') diff --git a/src/workflows/bidsSmoothing.m b/src/workflows/bidsSmoothing.m index ceaa5710..68aa6d81 100644 --- a/src/workflows/bidsSmoothing.m +++ b/src/workflows/bidsSmoothing.m @@ -24,7 +24,7 @@ function bidsSmoothing(funcFWHM, opt) printProcessingSubject(iSub, subLabel); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSmoothingFunc(matlabbatch, BIDS, opt, subLabel, funcFWHM); saveAndRunWorkflow(matlabbatch, ['smoothing_FWHM-' num2str(funcFWHM)], opt, subLabel); diff --git a/src/workflows/bidsSpatialPrepro.m b/src/workflows/bidsSpatialPrepro.m index b144192c..a62b081a 100644 --- a/src/workflows/bidsSpatialPrepro.m +++ b/src/workflows/bidsSpatialPrepro.m @@ -47,9 +47,9 @@ function bidsSpatialPrepro(opt) opt.orderBatches.skullStripping = 6; opt.orderBatches.skullStrippingMask = 7; - parfor iSub = 1:numel(opt.subjects) + for iSub = 1:numel(opt.subjects) - matlabbatch = []; + matlabbatch = {}; subLabel = opt.subjects{iSub}; diff --git a/src/workflows/bidsWholeBrainFuncMask.m b/src/workflows/bidsWholeBrainFuncMask.m new file mode 100644 index 00000000..28e88652 --- /dev/null +++ b/src/workflows/bidsWholeBrainFuncMask.m @@ -0,0 +1,39 @@ +function mask = bidsWholeBrainFuncMask(opt) + % + % create segmented-skull stripped mean functional image + % + % (C) Copyright 2020 CPP_SPM developers + + [BIDS, opt] = setUpWorkflow(opt, 'create brain mask from mean functional image'); + + mask = cell(numel(opt.subjects), 1); + + for iSub = 1:numel(opt.subjects) + + subLabel = opt.subjects{iSub}; + + % call/create the mask name + [meanImage, meanFuncDir] = getMeanFuncFilename(BIDS, subLabel, opt); + meanFuncFileName = fullfile(meanFuncDir, meanImage); + + % name the output accordingto the input image + maskFileName = ['m' strrep(meanImage, '.nii', '_mask.nii')]; + mask{iSub} = fullfile(meanFuncDir, maskFileName); + + % ask if mask exist, if not create it: + if ~exist(mask{iSub}, 'file') + + % set batch order since there is dependencies + opt.orderBatches.segment = 1; + opt.orderBatches.skullStripping = 2; + + % make matlab batch for segment and skullstip + matlabbatch = {}; + matlabbatch = setBatchSegmentation(matlabbatch, opt, meanFuncFileName); + + matlabbatch = setBatchSkullStripping(matlabbatch, BIDS, opt, subLabel); + % run spm + saveAndRunWorkflow(matlabbatch, 'meanImage_segment_skullstrip', opt, subLabel); + end + + end diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..35e101db --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,5 @@ +# test folder and dummy data +*.png +*/*.json +group/* +dummyData/derivatives/cpp_spm*/sub-*/ \ No newline at end of file diff --git a/tests/createDummyDataSet.sh b/tests/createDummyDataSet.sh index d38a692e..7a1ea639 100755 --- a/tests/createDummyDataSet.sh +++ b/tests/createDummyDataSet.sh @@ -42,6 +42,7 @@ do touch $ThisDir/sub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii touch $ThisDir/meanusub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii + touch $ThisDir/wmeanusub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii touch $ThisDir/s6wsub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii touch $ThisDir/s6rsub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii touch $ThisDir/s6usub-$Subject\_ses-$Ses\_task-vislocalizer_bold.nii diff --git a/tests/dummyData/mat_files/onsets.mat b/tests/dummyData/mat_files/onsets.mat new file mode 100644 index 00000000..ada01389 Binary files /dev/null and b/tests/dummyData/mat_files/onsets.mat differ diff --git a/tests/test_checkOptions.m b/tests/test_checkOptions.m index 383c71f0..76ac996e 100644 --- a/tests/test_checkOptions.m +++ b/tests/test_checkOptions.m @@ -55,7 +55,7 @@ function test_checkOptionsErrorGroup() function test_checkOptionsErrorRefSlice() - opt.STC_referenceSlice = [1:10]; + opt.stc.referenceSlice = [1:10]; opt.taskName = 'testTask'; assertExceptionThrown( ... diff --git a/tests/test_createAndReturnOnsetFile.m b/tests/test_createAndReturnOnsetFile.m index 6c10563f..c98d8bfe 100644 --- a/tests/test_createAndReturnOnsetFile.m +++ b/tests/test_createAndReturnOnsetFile.m @@ -34,4 +34,13 @@ function test_createAndReturnOnsetFileBasic() assertEqual(exist(onsetFileName, 'file'), 2); assertEqual(exist(expectedFileName, 'file'), 2); + expected_content = load(fullfile(fileparts(mfilename('fullpath')), ... + 'dummyData', ... + 'mat_files', ... + 'onsets.mat')); + + actual_content = load(onsetFileName); + + assertEqual(actual_content, expected_content); + end diff --git a/tests/test_getMeanFuncFilename.m b/tests/test_getMeanFuncFilename.m index 5e7de78d..a0df8c56 100644 --- a/tests/test_getMeanFuncFilename.m +++ b/tests/test_getMeanFuncFilename.m @@ -18,7 +18,7 @@ function test_getMeanFuncFilenameBasic() [meanImage, meanFuncDir] = getMeanFuncFilename(BIDS, subLabel, opt); - expectedMeanImage = 'meanusub-01_ses-01_task-vislocalizer_bold.nii'; + expectedMeanImage = 'wmeanusub-01_ses-01_task-vislocalizer_bold.nii'; expectedmeanFuncDir = fullfile(fileparts(mfilename('fullpath')), ... 'dummyData', 'derivatives', 'cpp_spm', ... diff --git a/tests/test_setBatch3Dto4D.m b/tests/test_setBatch3Dto4D.m index cd21d71f..6c9d7f34 100644 --- a/tests/test_setBatch3Dto4D.m +++ b/tests/test_setBatch3Dto4D.m @@ -16,7 +16,7 @@ function test_setBatch3Dto4DBasic() RT = 2; - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatch3Dto4D(matlabbatch, volumesList, RT); expectedBatch{1}.spm.util.cat.vols = volumesList; diff --git a/tests/test_setBatchComputeVDM.m b/tests/test_setBatchComputeVDM.m index 0efe5e5e..dc4de15e 100644 --- a/tests/test_setBatchComputeVDM.m +++ b/tests/test_setBatchComputeVDM.m @@ -13,7 +13,7 @@ function test_setBatchComputeVDMBasic() fmapType = 'phasediff'; refImage = fullfile(pwd, 'mean_sub-01-task-rest_bold.nii'); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchComputeVDM(matlabbatch, fmapType, refImage); expectedBatch = returnExpectedBatch(refImage); diff --git a/tests/test_setBatchFactorialDesign.m b/tests/test_setBatchFactorialDesign.m index e5fcd5ba..b3b7f7bd 100644 --- a/tests/test_setBatchFactorialDesign.m +++ b/tests/test_setBatchFactorialDesign.m @@ -16,7 +16,7 @@ function test_setBatchFactorialDesignBasic() opt = setOptions('vismotion'); opt.subjects = {'01' '02'}; - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchFactorialDesign(matlabbatch, opt, funcFWHM, conFWHM); % TODO diff --git a/tests/test_setBatchImageCalculation.m b/tests/test_setBatchImageCalculation.m index 0e70d94c..5db3da41 100644 --- a/tests/test_setBatchImageCalculation.m +++ b/tests/test_setBatchImageCalculation.m @@ -13,16 +13,24 @@ function test_setBatchImageCalculationBasic() input = {'sub-01_ses-01_T1w.nii'}; output = 'sub-01_ses-01_T1w_thres.nii'; outDir = pwd; - expression = 'i1 > 10'; + exp = 'i1 > 10'; - matlabbatch = []; - matlabbatch = setBatchImageCalculation(matlabbatch, input, output, outDir, expression); + matlabbatch = {}; + matlabbatch = setBatchImageCalculation(matlabbatch, input, output, outDir, exp, 'uint8'); expectedBatch{1}.spm.util.imcalc.input{1} = 'sub-01_ses-01_T1w.nii'; expectedBatch{end}.spm.util.imcalc.output = 'sub-01_ses-01_T1w_thres.nii'; expectedBatch{end}.spm.util.imcalc.outdir{1} = pwd; expectedBatch{end}.spm.util.imcalc.expression = 'i1 > 10'; + expectedBatch{end}.spm.util.imcalc.options.dtype = 2; assertEqual(matlabbatch, expectedBatch); + assertExceptionThrown(@()setBatchImageCalculation(matlabbatch, ... + input, ... + output, ... + outDir, ... + exp, ... + 'test'), ... + 'setBatchImageCalculation:invalidDatatype'); end diff --git a/tests/test_setBatchMeanAnatAndMask.m b/tests/test_setBatchMeanAnatAndMask.m index 6b15f9fd..4e2bd366 100644 --- a/tests/test_setBatchMeanAnatAndMask.m +++ b/tests/test_setBatchMeanAnatAndMask.m @@ -15,7 +15,7 @@ function test_setBatchMeanAnatAndMaskBasic() opt = setOptions('vismotion'); opt.subjects = {'01', '02'}; - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchMeanAnatAndMask(matlabbatch, opt, funcFWHM, pwd); % @@ -33,6 +33,7 @@ function test_setBatchMeanAnatAndMaskBasic() imcalc.output = 'meanAnat.nii'; imcalc.outdir{1} = pwd; imcalc.expression = '(i1+i2)/2'; + imcalc.options.dtype = 16; expectedBatch{1}.spm.util.imcalc = imcalc; @@ -50,6 +51,7 @@ function test_setBatchMeanAnatAndMaskBasic() imcalc.output = 'meanMask.nii'; imcalc.outdir{1} = pwd; imcalc.expression = '(i1+i2)>0.75*2'; + imcalc.options.dtype = 16; expectedBatch{2}.spm.util.imcalc = imcalc; diff --git a/tests/test_setBatchRealign.m b/tests/test_setBatchRealign.m index cc48d55c..3e4b7c16 100644 --- a/tests/test_setBatchRealign.m +++ b/tests/test_setBatchRealign.m @@ -21,7 +21,7 @@ function test_setBatchRealignBasic() opt = setOptions('MoAE', subLabel); [BIDS, opt] = getData(opt); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchRealign(matlabbatch, BIDS, opt, subLabel); expectedBatch{1}.spm.spatial.realignunwarp.eoptions.weight = {''}; diff --git a/tests/test_setBatchResults.m b/tests/test_setBatchResults.m index 490577ae..930c43d8 100644 --- a/tests/test_setBatchResults.m +++ b/tests/test_setBatchResults.m @@ -23,7 +23,7 @@ function test_setBatchResultsBasic() result.Contrasts.p = 0.05; result.Contrasts.k = 0; - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchResults(matlabbatch, result); expectedBatch = returnBasicExpectedResultsBatch(); @@ -74,7 +74,7 @@ function test_setBatchResultsExport() result.Output = opt.result.Steps(iStep).Output; result.space = opt.space; - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchResults(matlabbatch, result); expectedBatch = returnBasicExpectedResultsBatch(); @@ -124,7 +124,7 @@ function test_setBatchResultsMontage() result.Output = opt.result.Steps(iStep).Output; result.space = opt.space; - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchResults(matlabbatch, result); expectedBatch = returnBasicExpectedResultsBatch(); diff --git a/tests/test_setBatchSTC.m b/tests/test_setBatchSTC.m index 85817da4..368377c0 100644 --- a/tests/test_setBatchSTC.m +++ b/tests/test_setBatchSTC.m @@ -8,6 +8,22 @@ initTestSuite; end +function test_setBatchSTCSkip() + + subLabel = '02'; + + opt = setOptions('vislocalizer', subLabel); + + [BIDS, opt] = getData(opt); + + opt.stc.skip = true; + + matlabbatch = {}; + matlabbatch = setBatchSTC(matlabbatch, BIDS, opt, subLabel); + assertEqual(matlabbatch, {}); + +end + function test_setBatchSTCEmpty() subLabel = '02'; @@ -16,11 +32,11 @@ function test_setBatchSTCEmpty() [BIDS, opt] = getData(opt); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSTC(matlabbatch, BIDS, opt, subLabel); % no slice timing info for this run so nothing should be returned. - assertEqual(matlabbatch, []); + assertEqual(matlabbatch, {}); end @@ -31,19 +47,19 @@ function test_setBatchSTCForce() opt = setOptions('vislocalizer', subLabel); % we give it some slice timing value to force slice timing to happen - opt.sliceOrder = linspace(0, 1.6, 10); - opt.sliceOrder(end - 1:end) = []; - opt.STC_referenceSlice = 1.6 / 2; + opt.stc.sliceOrder = linspace(0, 1.6, 10); + opt.stc.sliceOrder(end - 1:end) = []; + opt.stc.referenceSlice = 1.6 / 2; opt = checkOptions(opt); [BIDS, opt] = getData(opt); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSTC(matlabbatch, BIDS, opt, subLabel); TR = 1.55; - expectedBatch = returnExpectedBatch(opt.sliceOrder, opt.STC_referenceSlice, TR); + expectedBatch = returnExpectedBatch(opt.stc.sliceOrder, opt.stc.referenceSlice, TR); runCounter = 1; for iSes = 1:2 @@ -69,7 +85,7 @@ function test_setBatchSTCBasic() [BIDS, opt] = getData(opt); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSTC(matlabbatch, BIDS, opt, subLabel); TR = 1.5; @@ -77,9 +93,9 @@ function test_setBatchSTCBasic() 0.5475, 0, 0.3825, 0.055, 0.4375, 0.11, 0.4925, 0.22, 0.6025, ... 0.275, 0.6575, ... 0.3275, 0.71, 0.165], 1, 3)'; - STC_referenceSlice = 0.355; + referenceSlice = 0.355; - expectedBatch = returnExpectedBatch(sliceOrder, STC_referenceSlice, TR); + expectedBatch = returnExpectedBatch(sliceOrder, referenceSlice, TR); runCounter = 1; for iSes = 1:2 @@ -106,14 +122,13 @@ function test_setBatchSTCErrorInvalidInputTime() opt = setOptions('vislocalizer', subLabel); - opt.sliceOrder = linspace(0, 1.6, 10); - opt.sliceOrder(end) = []; - opt.STC_referenceSlice = 2; % impossible reference value + opt.stc.sliceOrder = linspace(0, 1.6, 10); + opt.stc.sliceOrder(end) = []; + opt.stc.referenceSlice = 2; % impossible reference value [BIDS, opt] = getData(opt); - matlabbatch = []; - + matlabbatch = {}; assertExceptionThrown( ... @()setBatchSTC(matlabbatch, BIDS, opt, subLabel), ... 'setBatchSTC:invalidInputTime'); @@ -126,10 +141,12 @@ function test_setBatchSTCErrorInvalidInputTime() TA = TR - (TR / nbSlices); TA = ceil(TA * 1000) / 1000; - expectedBatch{1}.spm.temporal.st.nslices = nbSlices; - expectedBatch{1}.spm.temporal.st.tr = TR; - expectedBatch{1}.spm.temporal.st.ta = TA; - expectedBatch{1}.spm.temporal.st.so = sliceOrder * 1000; - expectedBatch{1}.spm.temporal.st.refslice = referenceSlice * 1000; + temporal.st.nslices = nbSlices; + temporal.st.tr = TR; + temporal.st.ta = TA; + temporal.st.so = sliceOrder * 1000; + temporal.st.refslice = referenceSlice * 1000; + + expectedBatch{1}.spm.temporal = temporal; end diff --git a/tests/test_setBatchSegmentation.m b/tests/test_setBatchSegmentation.m index 91872c4d..66076a51 100644 --- a/tests/test_setBatchSegmentation.m +++ b/tests/test_setBatchSegmentation.m @@ -17,7 +17,7 @@ function test_setBatchSegmentationPipeline() opt.orderBatches.selectAnat = 1; - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSegmentation(matlabbatch, opt); expectedBatch = returnExpectedBatch(spmLocation); @@ -43,7 +43,7 @@ function test_setBatchSegmentationImages() anatImage = returnLocalAnatFilename(); % check with one file - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSegmentation(matlabbatch, opt, anatImage); expectedBatch = returnExpectedBatch(spmLocation); expectedBatch{end}.spm.spatial.preproc.channel.vols{1} = anatImage; @@ -51,7 +51,7 @@ function test_setBatchSegmentationImages() assertEqual(expectedBatch, matlabbatch); % check with several files passed as a cell - matlabbatch = []; + matlabbatch = {}; anatImage = {anatImage; anatImage}; matlabbatch = setBatchSegmentation(matlabbatch, opt, anatImage); expectedBatch{end}.spm.spatial.preproc.channel.vols = anatImage; diff --git a/tests/test_setBatchSelectAnat.m b/tests/test_setBatchSelectAnat.m index 083a9c32..19cd1dd2 100644 --- a/tests/test_setBatchSelectAnat.m +++ b/tests/test_setBatchSelectAnat.m @@ -20,7 +20,7 @@ function test_setBatchSelectAnatBasic() [BIDS, opt] = getData(opt); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSelectAnat(matlabbatch, BIDS, opt, subLabel); expectedBatch{1}.cfg_basicio.cfg_named_file.name = 'Anatomical'; diff --git a/tests/test_setBatchSkullStripping.m b/tests/test_setBatchSkullStripping.m index 921c227f..fe893f8b 100644 --- a/tests/test_setBatchSkullStripping.m +++ b/tests/test_setBatchSkullStripping.m @@ -18,16 +18,16 @@ function test_setBatchSkullStrippingBasic() opt.orderBatches.segment = 2; - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSkullStripping(matlabbatch, BIDS, opt, subLabel); - expectedBatch = returnExpectedBatch(opt); + expected_batch = returnExpectedBatch(opt); - assertEqual(matlabbatch, expectedBatch); + assertEqual(matlabbatch, expected_batch); end -function expectedBatch = returnExpectedBatch(opt) +function expected_batch = returnExpectedBatch(opt) expectedFileName = 'sub-01_ses-01_T1w.nii'; @@ -35,68 +35,57 @@ function test_setBatchSkullStrippingBasic() 'dummyData', 'derivatives', 'cpp_spm', ... 'sub-01', 'ses-01', 'anat'); - expectedBatch = []; - expectedBatch{end + 1}.spm.util.imcalc.input(1) = ... - cfg_dep( ... - 'Segment: Bias Corrected (1)', ... - substruct( ... - '.', 'val', '{}', {2}, ... - '.', 'val', '{}', {1}, ... - '.', 'val', '{}', {1}), ... - substruct( ... - '.', 'channel', '()', {1}, ... - '.', 'biascorr', '()', {':'})); - - expectedBatch{end}.spm.util.imcalc.input(2) = ... - cfg_dep( ... - 'Segment: c1 Images', ... - substruct( ... - '.', 'val', '{}', {2}, ... - '.', 'val', '{}', {1}, ... - '.', 'val', '{}', {1}), ... - substruct( ... - '.', 'tiss', '()', {1}, ... - '.', 'c', '()', {':'})); - - expectedBatch{end}.spm.util.imcalc.input(3) = ... - cfg_dep( ... - 'Segment: c2 Images', ... - substruct( ... - '.', 'val', '{}', {2}, ... - '.', 'val', '{}', {1}, ... - '.', 'val', '{}', {1}), ... - substruct( ... - '.', 'tiss', '()', {2}, ... - '.', 'c', '()', {':'})); - expectedBatch{end}.spm.util.imcalc.input(4) = ... - cfg_dep( ... - 'Segment: c3 Images', ... - substruct( ... - '.', 'val', '{}', {2}, ... - '.', 'val', '{}', {1}, ... - '.', 'val', '{}', {1}), ... - substruct( ... - '.', 'tiss', '()', {3}, ... - '.', 'c', '()', {':'})); - - expectedBatch{end}.spm.util.imcalc.output = ['m' strrep( ... - expectedFileName, ... - '.nii', ... - '_skullstripped.nii')]; - expectedBatch{end}.spm.util.imcalc.outdir = {expectedAnatDataDir}; - - expectedBatch{end}.spm.util.imcalc.expression = sprintf( ... - 'i1.*((i2+i3+i4)>%f)', ... - opt.skullstrip.threshold); + imcalc.input(1) = cfg_dep( ... + 'Segment: Bias Corrected (1)', ... + substruct( ... + '.', 'val', '{}', {2}, ... + '.', 'val', '{}', {1}, ... + '.', 'val', '{}', {1}), ... + substruct( ... + '.', 'channel', '()', {1}, ... + '.', 'biascorr', '()', {':'})); + + imcalc.input(2) = cfg_dep( ... + 'Segment: c1 Images', ... + substruct( ... + '.', 'val', '{}', {2}, ... + '.', 'val', '{}', {1}, ... + '.', 'val', '{}', {1}), ... + substruct( ... + '.', 'tiss', '()', {1}, ... + '.', 'c', '()', {':'})); + + imcalc.input(3) = cfg_dep( ... + 'Segment: c2 Images', ... + substruct( ... + '.', 'val', '{}', {2}, ... + '.', 'val', '{}', {1}, ... + '.', 'val', '{}', {1}), ... + substruct( ... + '.', 'tiss', '()', {2}, ... + '.', 'c', '()', {':'})); + imcalc.input(4) = cfg_dep( ... + 'Segment: c3 Images', ... + substruct( ... + '.', 'val', '{}', {2}, ... + '.', 'val', '{}', {1}, ... + '.', 'val', '{}', {1}), ... + substruct( ... + '.', 'tiss', '()', {3}, ... + '.', 'c', '()', {':'})); + + imcalc.output = ['m' strrep(expectedFileName, '.nii', '_skullstripped.nii')]; + imcalc.outdir = {expectedAnatDataDir}; + imcalc.expression = sprintf('i1.*((i2+i3+i4)>%f)', opt.skullstrip.threshold); + imcalc.options.dtype = 16; + + expected_batch = {}; + expected_batch{end + 1}.spm.util.imcalc = imcalc; % add a batch to output the mask - expectedBatch{end + 1} = expectedBatch{end}; - expectedBatch{end}.spm.util.imcalc.expression = sprintf( ... - '(i2+i3+i4)>%f', ... - opt.skullstrip.threshold); - expectedBatch{end}.spm.util.imcalc.output = ['m' strrep( ... - expectedFileName, ... - '.nii', ... - '_mask.nii')]; + imcalc.expression = sprintf('(i2+i3+i4)>%f', opt.skullstrip.threshold); + imcalc.output = ['m' strrep(expectedFileName, '.nii', '_mask.nii')]; + + expected_batch{end + 1}.spm.util.imcalc = imcalc; end diff --git a/tests/test_setBatchSmoothConImages.m b/tests/test_setBatchSmoothConImages.m index 98357b9c..78247307 100644 --- a/tests/test_setBatchSmoothConImages.m +++ b/tests/test_setBatchSmoothConImages.m @@ -18,7 +18,7 @@ function test_setBatchSmoothConImagesBasic() [~, opt] = getData(opt); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSmoothConImages(matlabbatch, opt, funcFWHM, conFWHM); expectedBatch{1}.spm.spatial.smooth.fwhm = [6 6 6]; diff --git a/tests/test_setBatchSmoothing.m b/tests/test_setBatchSmoothing.m index f1016267..8fea8674 100644 --- a/tests/test_setBatchSmoothing.m +++ b/tests/test_setBatchSmoothing.m @@ -15,7 +15,7 @@ function test_setBatchSmoothingBasic() images = { fullfile(pwd, 'sub-01_T1w.nii') }; - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSmoothing(matlabbatch, images, FWHM, prefix); expectedBatch{1}.spm.spatial.smooth.fwhm = [6 6 6]; diff --git a/tests/test_setBatchSmoothingFunc.m b/tests/test_setBatchSmoothingFunc.m index 564f93ed..8c1c65d2 100644 --- a/tests/test_setBatchSmoothingFunc.m +++ b/tests/test_setBatchSmoothingFunc.m @@ -34,7 +34,7 @@ function test_setBatchSmoothingFuncBasic() filename ext]); system(sprintf('touch %s', fileName)); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSmoothingFunc(matlabbatch, BIDS, opt, subLabel, funcFWHM); expectedBatch{1}.spm.spatial.smooth.fwhm = [6 6 6]; diff --git a/tests/test_setBatchSubjectLevelContrasts.m b/tests/test_setBatchSubjectLevelContrasts.m index e2d3c548..e3fd8af7 100644 --- a/tests/test_setBatchSubjectLevelContrasts.m +++ b/tests/test_setBatchSubjectLevelContrasts.m @@ -15,7 +15,7 @@ function test_setBatchSubjectLevelContrastsBasic() opt = setOptions('vismotion', subLabel); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSubjectLevelContrasts(matlabbatch, opt, subLabel, funcFWHM); expectedBatch = []; diff --git a/tests/test_setBatchSubjectLevelGLMSpec.m b/tests/test_setBatchSubjectLevelGLMSpec.m index 94347c23..d513f245 100644 --- a/tests/test_setBatchSubjectLevelGLMSpec.m +++ b/tests/test_setBatchSubjectLevelGLMSpec.m @@ -34,7 +34,7 @@ function test_setBatchSubjectLevelGLMSpecBasic() system(sprintf('touch %s', ... fullfile(subFuncDataDir, ['rp_', strrep(fileName, '.nii', '.txt')]))); - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSubjectLevelGLMSpec(matlabbatch, BIDS, opt, subLabel, funcFWHM); % TODO add assert diff --git a/tests/test_setBatchSubjectLevelResults.m b/tests/test_setBatchSubjectLevelResults.m index 6ca6d728..71650c2b 100644 --- a/tests/test_setBatchSubjectLevelResults.m +++ b/tests/test_setBatchSubjectLevelResults.m @@ -21,7 +21,7 @@ function test_setBatchSubjectLevelResultsBasic() opt.result.Steps.Contrasts.Name = 'VisMot'; - matlabbatch = []; + matlabbatch = {}; matlabbatch = setBatchSubjectLevelResults(matlabbatch, opt, subLabel, funcFWHM, iStep, iCon); expectedBatch = {}; @@ -59,7 +59,7 @@ function test_setBatchSubjectLevelResultsErrorMissingContrastName() opt = setOptions('vismotion', subLabel); opt.space = 'MNI'; - matlabbatch = []; + matlabbatch = {}; assertExceptionThrown( ... @()setBatchSubjectLevelResults(matlabbatch, ... opt, ... @@ -87,7 +87,7 @@ function test_setBatchSubjectLevelResultsErrorNoMAtchingContrast() opt.result.Steps.Contrasts.Name = 'NotAContrast'; - matlabbatch = []; + matlabbatch = {}; assertExceptionThrown( ... @()setBatchSubjectLevelResults(matlabbatch, ... opt, ... diff --git a/tests/test_unit_getPrefix.m b/tests/test_unit_getPrefix.m index 619a9e91..bb9ba594 100644 --- a/tests/test_unit_getPrefix.m +++ b/tests/test_unit_getPrefix.m @@ -8,13 +8,30 @@ initTestSuite; end +function test_getPrefixSkipSTC() + + step = 'realign'; + + opt.metadata.SliceTiming = 1:0.2:1.8; + opt.stc.sliceOrder = 1:10; + opt.stc.skip = true; + opt = checkOptions(opt); + + prefix = getPrefix(step, opt); + + expectedPrefixOutput = ''; + + assertEqual(prefix, expectedPrefixOutput); + +end + function test_getPrefixSTC() step = 'realign'; - funcFWHM = 6; opt.metadata.SliceTiming = 1:0.2:1.8; - opt.sliceOrder = 1:10; + opt.stc.sliceOrder = 1:10; + opt = checkOptions(opt); [prefix, motionRegressorPrefix] = getPrefix(step, opt); @@ -31,7 +48,7 @@ function test_getPrefixNoSTC() step = 'realign'; opt.metadata = []; - opt.sliceOrder = []; + opt = checkOptions(opt); prefix = getPrefix(step, opt); @@ -46,13 +63,13 @@ function test_getPrefixMean() step = 'mean'; opt.metadata = []; - opt.sliceOrder = []; opt.realign.useUnwarp = true; opt.space = 'MNI'; + opt = checkOptions(opt); prefix = getPrefix(step, opt); - expectedPrefixOutput = ['mean' spm_get_defaults('unwarp.write.prefix')]; + expectedPrefixOutput = ['wmean' spm_get_defaults('unwarp.write.prefix')]; assertEqual(prefix, expectedPrefixOutput); @@ -60,13 +77,14 @@ function test_getPrefixMean() step = 'mean'; opt.metadata.SliceTiming = 1:0.2:1.8; - opt.sliceOrder = 1:10; + opt.stc.sliceOrder = 1:10; opt.realign.useUnwarp = false; opt.space = 'MNI'; + opt = checkOptions(opt); prefix = getPrefix(step, opt); - expectedPrefixOutput = 'meana'; + expectedPrefixOutput = 'wmeana'; assertEqual(prefix, expectedPrefixOutput); @@ -76,9 +94,9 @@ function test_getPrefixNormalise() step = 'normalise'; opt.metadata = []; - opt.sliceOrder = []; opt.realign.useUnwarp = true; opt.space = 'MNI'; + opt = checkOptions(opt); prefix = getPrefix(step, opt); @@ -90,9 +108,9 @@ function test_getPrefixNormalise() step = 'normalise'; opt.metadata = []; - opt.sliceOrder = []; opt.realign.useUnwarp = false; opt.space = 'MNI'; + opt = checkOptions(opt); prefix = getPrefix(step, opt); @@ -106,9 +124,9 @@ function test_getPrefixFuncQA() step = 'funcQA'; opt.metadata = []; - opt.sliceOrder = []; opt.realign.useUnwarp = true; opt.space = 'MNI'; + opt = checkOptions(opt); prefix = getPrefix(step, opt); @@ -120,9 +138,9 @@ function test_getPrefixFuncQA() step = 'funcQA'; opt.metadata = []; - opt.sliceOrder = []; opt.realign.useUnwarp = false; opt.space = 'MNI'; + opt = checkOptions(opt); prefix = getPrefix(step, opt); @@ -136,9 +154,9 @@ function test_getPrefixSmooth() step = 'smooth'; opt.metadata = []; - opt.sliceOrder = []; opt.realign.useUnwarp = true; opt.space = 'MNI'; + opt = checkOptions(opt); prefix = getPrefix(step, opt); @@ -151,6 +169,7 @@ function test_getPrefixSmooth() %% native space opt.realign.useUnwarp = true; opt.space = 'individual'; + opt = checkOptions(opt); prefix = getPrefix(step, opt); @@ -161,6 +180,7 @@ function test_getPrefixSmooth() %% native space no unwarp opt.realign.useUnwarp = false; opt.space = 'individual'; + opt = checkOptions(opt); prefix = getPrefix(step, opt); @@ -171,6 +191,7 @@ function test_getPrefixSmooth() %% MNI space no unwarp opt.realign.useUnwarp = false; opt.space = 'MNI'; + opt = checkOptions(opt); prefix = getPrefix(step, opt); @@ -185,9 +206,9 @@ function test_getPrefixFFX() step = 'FFX'; funcFWHM = 6; opt.metadata = []; - opt.sliceOrder = []; opt.realign.useUnwarp = true; opt.space = 'MNI'; + opt = checkOptions(opt); [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM); @@ -204,6 +225,7 @@ function test_getPrefixFFX() %% native space opt.realign.useUnwarp = true; opt.space = 'individual'; + opt = checkOptions(opt); [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM); @@ -220,7 +242,8 @@ function test_getPrefixFFX() opt.realign.useUnwarp = false; opt.space = 'individual'; opt.metadata.SliceTiming = 1:0.2:1.8; - opt.sliceOrder = 1:10; + opt.stc.sliceOrder = 1:10; + opt = checkOptions(opt); [prefix, motionRegressorPrefix] = getPrefix(step, opt, funcFWHM); @@ -238,14 +261,15 @@ function test_getPrefixFFX() opt.realign.useUnwarp = false; opt.space = 'MNI'; opt.metadata = []; - opt.sliceOrder = []; + opt = checkOptions(opt); [prefix] = getPrefix(step, opt, funcFWHM); expectedPrefixOutput = sprintf('%s%i%s%s', ... spm_get_defaults('smooth.prefix'), ... funcFWHM, ... - spm_get_defaults('normalise.write.prefix')); + spm_get_defaults('normalise.write.prefix'), ... + spm_get_defaults('slicetiming.prefix')); expectedMotionRegressorPrefix = spm_get_defaults('slicetiming.prefix'); assertEqual(prefix, expectedPrefixOutput); @@ -257,7 +281,7 @@ function test_getPrefixError() step = 'error'; funcFWHM = 6; opt.metadata = []; - opt.sliceOrder = []; + opt = checkOptions(opt); assertExceptionThrown( ... @()getPrefix(step, opt, funcFWHM), ... diff --git a/tests/test_unit_getSliceOrder.m b/tests/test_unit_getSliceOrder.m index 543c141e..6a43a199 100644 --- a/tests/test_unit_getSliceOrder.m +++ b/tests/test_unit_getSliceOrder.m @@ -51,11 +51,11 @@ function test_getSliceOrderEmpty() function test_getSliceOrderFromOptions() opt = setOptions('vislocalizer'); - opt.STC_referenceSlice = 1000; - opt.sliceOrder = 0:250:2000; + opt.stc.referenceSlice = 1000; + opt.stc.sliceOrder = 0:250:2000; [~, opt] = getData(opt); BIDS_sliceOrder = getSliceOrder(opt, 0); - assert(isequal(BIDS_sliceOrder, opt.sliceOrder)); + assert(isequal(BIDS_sliceOrder, opt.stc.sliceOrder)); end diff --git a/tests/utils/defaultOptions.m b/tests/utils/defaultOptions.m index d258de43..a3a4fd3d 100644 --- a/tests/utils/defaultOptions.m +++ b/tests/utils/defaultOptions.m @@ -2,8 +2,9 @@ % % (C) Copyright 2021 CPP_SPM developers - expectedOptions.sliceOrder = []; - expectedOptions.STC_referenceSlice = []; + expectedOptions.stc.sliceOrder = []; + expectedOptions.stc.referenceSlice = []; + expectedOptions.stc.skip = false; expectedOptions.dataDir = ''; expectedOptions.derivativesDir = ''; @@ -23,6 +24,7 @@ expectedOptions.anatReference.session = []; expectedOptions.skullstrip.threshold = 0.75; + expectedOptions.skullstrip.mean = false; expectedOptions.realign.useUnwarp = true; expectedOptions.useFieldmaps = true; @@ -33,7 +35,15 @@ expectedOptions.contrastList = {}; - expectedOptions.glm.QA.do = true; + expectedOptions.QA.glm.do = true; + expectedOptions.QA.func.carpetPlot = true; + expectedOptions.QA.func.Motion = 'on'; + expectedOptions.QA.func.FD = 'on'; + expectedOptions.QA.func.Voltera = 'on'; + expectedOptions.QA.func.Globals = 'on'; + expectedOptions.QA.func.Movie = 'on'; + expectedOptions.QA.func.Basics = 'on'; + expectedOptions.glm.roibased.do = false; expectedOptions.model.file = ''; @@ -43,12 +53,20 @@ expectedOptions.parallelize.do = false; expectedOptions.parallelize.nbWorkers = 3; - expectedOptions.parallelize.killOnExit = true; + expectedOptions.parallelize.killOnExit = false; if nargin > 0 expectedOptions.taskName = taskName; end + % Options for toolboxes + global ALI_TOOLBOX_PRESENT + + checkToolbox('ALI'); + if ALI_TOOLBOX_PRESENT + expectedOptions = setFields(expectedOptions, ALI_my_defaults()); + end + expectedOptions = orderfields(expectedOptions); expectedOptions = setStatsDir(expectedOptions); diff --git a/version.txt b/version.txt index 60453e69..992977ad 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v1.0.0 \ No newline at end of file +v1.1.0 \ No newline at end of file