diff --git a/NIfTI_20140122/FAQ.pdf b/NIfTI_20140122/FAQ.pdf new file mode 100644 index 0000000..b04456b Binary files /dev/null and b/NIfTI_20140122/FAQ.pdf differ diff --git a/NIfTI_20140122/NIfTI_tools.pdf b/NIfTI_20140122/NIfTI_tools.pdf new file mode 100644 index 0000000..592c807 Binary files /dev/null and b/NIfTI_20140122/NIfTI_tools.pdf differ diff --git a/NIfTI_20140122/UseANALYZE.pdf b/NIfTI_20140122/UseANALYZE.pdf new file mode 100644 index 0000000..4bcdf21 Binary files /dev/null and b/NIfTI_20140122/UseANALYZE.pdf differ diff --git a/NIfTI_20140122/affine.m b/NIfTI_20140122/affine.m new file mode 100644 index 0000000..13b652f --- /dev/null +++ b/NIfTI_20140122/affine.m @@ -0,0 +1,554 @@ +% 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 +% integer or floating-point. +% +% You may notice that MATLAB has a function called 'imtransform.m' for +% 2D spatial transformation. However, keep in mind that 'imtransform.m' +% assumes y for the 1st dimension, and x for the 2nd dimension. They are +% equivalent otherwise. +% +% In addition, if you adjust the 'new_elem_size' parameter, this 'affine.m' +% is equivalent to 'interp2.m' for 2D image, and equivalent to 'interp3.m' +% for 3D volume. +% +% Usage: [new_img new_M] = ... +% 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_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. +% +% 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. +% +% 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. +% +% new_img - transformed 2D image or 3D volume +% +% 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); +% +% 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); +% +% This program is inspired by: +% SPM5 Software from Wellcome Trust Centre for Neuroimaging +% 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 +% +% - 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 + case 1 + 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); + case 3 + new_img(:,:,z) = bresenham(old_img, new_dim, old_dim, M, bg); + end + + end; % for z + + if ndims(old_img) == 2 + new_M(3,:) = []; + new_M(:,3) = []; + end + + return; % affine + + +%-------------------------------------------------------------------- +function img_slice = trilinear(img, dim1, dim2, M, bg) + + img_slice = zeros(dim1(1:2)); + TINY = 5e-2; % tolerance + + % 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); + + % initialize new_Y accumulation + % + Y2X = 0; + Y2Y = 0; + Y2Z = 0; + + 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 + + % 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 + % + 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 + + +%-------------------------------------------------------------------- +function img_slice = nearest_neighbor(img, dim1, dim2, M, bg) + + img_slice = zeros(dim1(1: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); + + % initialize new_Y accumulation + % + Y2X = 0; + Y2Y = 0; + Y2Z = 0; + + 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 + + % 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 + % + 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); + + % within boundary of original image + % + if ( xi >= 1 & xi <= xdim2 & ... + yi >= 1 & yi <= ydim2 & ... + zi >= 1 & zi <= zdim2 ) + + img_slice(x,y) = img(xi,yi,zi); + + else + img_slice(x,y) = bg; + + end % if boundary + + end % for x + end % for y + + return; % nearest_neighbor + + +%-------------------------------------------------------------------- +function img_slice = bresenham(img, dim1, dim2, M, bg) + + img_slice = zeros(dim1(1: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); + + for y = 1:ydim1 + + 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); + + % 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 + + 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 + + xi = X(i); + yi = Y(i); + zi = Z(i); + + % within boundary of the old XYZ space + % + if ( xi >= 1 & xi <= xdim2 & ... + yi >= 1 & yi <= ydim2 & ... + zi >= 1 & zi <= zdim2 ) + + img_slice(x,y) = img(xi,yi,zi); + +% if line_error > 1 + % x = x + 1; + +% 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 + + else + img_slice(x,y) = bg; + + end % if boundary + + end % for x + end % for y + + return; % bresenham + diff --git a/NIfTI_20140122/bipolar.m b/NIfTI_20140122/bipolar.m new file mode 100644 index 0000000..0c79c37 --- /dev/null +++ b/NIfTI_20140122/bipolar.m @@ -0,0 +1,94 @@ +%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. +% +% Inspired from the LORETA PASCAL program: +% 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('lo','var') + lo = -1; + end + + if ~exist('hi','var') + hi = 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; + + doubleredc = []; + doublebluec = []; + + if lo >= 0 % all positive + + 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 + + cmap = doubleredc; + + elseif hi <= 0 % all negative + + 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 + + cmap = flipud(doublebluec); + + else + + 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, 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]; + + end + + return; % bipolar + diff --git a/NIfTI_20140122/bresenham_line3d.m b/NIfTI_20140122/bresenham_line3d.m new file mode 100644 index 0000000..d4db692 --- /dev/null +++ b/NIfTI_20140122/bresenham_line3d.m @@ -0,0 +1,189 @@ +% Generate X Y Z coordinates of a 3D Bresenham's line between +% two given points. +% +% A very useful application of this algorithm can be found in the +% implementation of Fischer's Bresenham interpolation method in my +% another program that can rotate three dimensional image volume +% with an affine matrix: +% http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=21080 +% +% Usage: [X Y Z] = bresenham_line3d(P1, P2, [precision]); +% +% P1 - vector for Point1, where P1 = [x1 y1 z1] +% +% 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. +% +% X - a set of x 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 +% +% 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'); +% +% This program is ported to MATLAB from: +% +% B.Pendleton. line3d - 3D Bresenham's (a 3D line drawing algorithm) +% ftp://ftp.isc.org/pub/usenet/comp.sources.unix/volume26/line3d, 1992 +% +% Which is also referenced by: +% +% 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 +% +% - 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; + 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; + 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; + end + end + + if precision ~= 0 + X = X/(10^precision); + Y = Y/(10^precision); + Z = Z/(10^precision); + end + + return; % bresenham_line3d + diff --git a/NIfTI_20140122/clip_nii.m b/NIfTI_20140122/clip_nii.m new file mode 100644 index 0000000..40fcb30 --- /dev/null +++ b/NIfTI_20140122/clip_nii.m @@ -0,0 +1,115 @@ +% CLIP_NII: Clip the NIfTI volume from any of the 6 sides +% +% Usage: nii = clip_nii(nii, [option]) +% +% Inputs: +% +% nii - NIfTI volume. +% +% 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 ) +% +% Options description in detail: +% ============================== +% +% 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_P: Number of voxels from Posterior 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_S: Number of voxels from Superior side will be clipped. +% +% NIfTI data format can be found on: http://nifti.nimh.nih.gov +% +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) +% +function nii = clip_nii(nii, opt) + + 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 + + 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 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 + end + + 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 + end + + 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 + end + + 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 + end + + 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 + end + + 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 + 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); + + return; + diff --git a/NIfTI_20140122/collapse_nii_scan.m b/NIfTI_20140122/collapse_nii_scan.m new file mode 100644 index 0000000..4c00078 --- /dev/null +++ b/NIfTI_20140122/collapse_nii_scan.m @@ -0,0 +1,260 @@ +% Collapse multiple single-scan NIFTI files into a multiple-scan NIFTI file +% +% Usage: collapse_nii_scan(scan_file_pattern, [collapsed_fileprefix], [scan_file_folder]) +% +% Here, scan_file_pattern should look like: 'myscan_0*.img' +% 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 +% corresponding filenames for those selected scan files. +% +% NIFTI data format can be found on: http://nifti.nimh.nih.gov +% +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) +% +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 + 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 + + 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 + + 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 + + % 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/NIfTI_20140122/examples.txt b/NIfTI_20140122/examples.txt new file mode 100644 index 0000000..82bec5f --- /dev/null +++ b/NIfTI_20140122/examples.txt @@ -0,0 +1,130 @@ + +- Examples to load, make and save a nii struct: + + To load Analyze data or NIFTI data to a structure: + + nii = load_nii(NIFTI_file_name, [img_idx], [old_RGB24]); + + img_idx is a numerical array of image indices along the temporal + axis, which is only available in NIFTI data. After you specify + img_idx, only those images indexed by img_idx will be loaded. If + there is no img_idx or img_idx is empty, all available images + will be loaded. + + For RGB image, most people use RGB triple sequentially for each + voxel, like [R1 G1 B1 R2 G2 B2 ...]. However, some program like + Analyze 6.0 developed by AnalyzeDirect uses old RGB24, in a way + like [R1 R2 ... G1 G2 ... B1 B2 ...] for each slices. In this + case, you can set old_RGB24 flag to 1 and load data correctly: + + nii = load_nii(NIFTI_file_name, [], 1); + + To get a total number of images along the temporal axis: + + num_scan = get_nii_frame(NIFTI_file_name); + + You can also load the header extension if it exists: + + nii.ext = load_nii_ext(NIFTI_file_name); + + You can just load the Analyze or NIFTI header: + (header contains: hk, dime, and hist) + + hdr = load_nii_hdr(NIFTI_file_name); + + You can also save the structure to a new file: + (header extension will be saved if there is nii.ext structure) + + save_nii(nii, NIFTI_file_name); + + To make the structure from any 3D (or 4D) data: + + img = rand(91,109,91); or + img = rand(64,64,21,18); + nii = make_nii(img [, voxel_size, origin, datatype] ); + + Use "help load_nii", "help save_nii", "help make_nii" etc. + to get more detail information. + + +- Examples to plot a nii struct: + (More detail descriptions are available on top of "view_nii.m") + + Simple way to plot a nii struct: + + view_nii(nii); + + The default colormap will use the Gray if all data values are + non-negative; otherwise, the default colormap will use BiPolar. + You can choose other colormap, including customized colormap + from panel. + + To imbed the plot into your existing figure: + + h = gcf; + opt.command = 'init'; + opt.setarea = [0.3 0.1 0.6 0.8]; + view_nii(h, nii, opt); + + To add a colorbar: + + opt.usecolorbar = 1; + view_nii(gcf, opt); + + Here, opt.command is implicitly set to 'update'. + + To display in real aspect ratio: + + opt.usestretch = 0; + view_nii(gcf, opt); + + If you want the data value to be directly used as the index + of colormap, instead of scale to the whole colormap: + + opt.useimagesc = 0; + view_nii(gcf, opt); + + If you modified the data value without changing the dimension, + voxel_size, and origin, you can update the display by: + + opt.command = 'updateimg'; + view_nii(gcf, nii.img, opt); + + If the data is completely different, display can be updated by: + + opt.command = 'updatenii'; + view_nii(gcf, nii, opt); + + This is an example to plot EEG source imaging on top of T1 background: + 1. download overlay.zip and unzip it from: + http://www.rotman-baycrest.on.ca/~jimmy/NIFTI/overlay.zip + 2. T1 = load_nii('T1.nii'); + 3. EEG = load_nii('EEG.nii'); + 4. option.setvalue.idx = find(EEG.img); + 5. option.setvalue.val = EEG.img(option.setvalue.idx); + 6. option.useinterp = 1; + 7. option.setviewpoint = [62 48 46]; + 8. view_nii(T1, option); + + +- Contrast and Brightness are available under Gray and Bipolar colormap: + + Increase contrast in Gray colormap will make high end values + more distinguishable by sacrificing the low end values; The + minimum contrast (default) will display the whole range. + + Increase or decrease contrast in BiPolar colormap will shift + the distinguishable position for both positive and negative + values. + + Increase or decrease brightness in Gray colormap will shift + the distinguishable position. + + Increase or decrease brightness in BiPolar colormap will make + both positive and negative values more distinguishable. + + +- Required files: + + All files in this package. + diff --git a/NIfTI_20140122/expand_nii_scan.m b/NIfTI_20140122/expand_nii_scan.m new file mode 100644 index 0000000..e3a3bf8 --- /dev/null +++ b/NIfTI_20140122/expand_nii_scan.m @@ -0,0 +1,48 @@ +% Expand a multiple-scan NIFTI file into multiple single-scan NIFTI files +% +% Usage: expand_nii_scan(multi_scan_filename, [img_idx], [path_to_save]) +% +% NIFTI data format can be found on: http://nifti.nimh.nih.gov +% +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) +% +function expand_nii_scan(filename, img_idx, newpath) + + 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 + 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); + + fn = [nii_i.fileprefix '_' sprintf('%04d',i)]; + pnfn = fullfile(newpath, fn); + + if exist('gzFile', 'var') + pnfn = [pnfn '.nii.gz']; + end + + save_untouch_nii(nii_i, pnfn); + end + + return; % expand_nii_scan + diff --git a/NIfTI_20140122/extra_nii_hdr.m b/NIfTI_20140122/extra_nii_hdr.m new file mode 100644 index 0000000..99a7f21 --- /dev/null +++ b/NIfTI_20140122/extra_nii_hdr.m @@ -0,0 +1,255 @@ +% Decode extra NIFTI header information into hdr.extra +% +% Usage: hdr = extra_nii_hdr(hdr) +% +% hdr can be obtained from load_nii_hdr +% +% NIFTI data format can be found on: http://nifti.nimh.nih.gov +% +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) +% +function hdr = extra_nii_hdr(hdr) + + switch hdr.dime.datatype + case 1 + extra.NIFTI_DATATYPES = 'DT_BINARY'; + case 2 + extra.NIFTI_DATATYPES = 'DT_UINT8'; + case 4 + extra.NIFTI_DATATYPES = 'DT_INT16'; + case 8 + extra.NIFTI_DATATYPES = 'DT_INT32'; + case 16 + extra.NIFTI_DATATYPES = 'DT_FLOAT32'; + case 32 + extra.NIFTI_DATATYPES = 'DT_COMPLEX64'; + case 64 + extra.NIFTI_DATATYPES = 'DT_FLOAT64'; + case 128 + extra.NIFTI_DATATYPES = 'DT_RGB24'; + case 256 + extra.NIFTI_DATATYPES = 'DT_INT8'; + case 512 + extra.NIFTI_DATATYPES = 'DT_UINT16'; + case 768 + extra.NIFTI_DATATYPES = 'DT_UINT32'; + case 1024 + extra.NIFTI_DATATYPES = 'DT_INT64'; + case 1280 + extra.NIFTI_DATATYPES = 'DT_UINT64'; + case 1536 + extra.NIFTI_DATATYPES = 'DT_FLOAT128'; + case 1792 + extra.NIFTI_DATATYPES = 'DT_COMPLEX128'; + case 2048 + extra.NIFTI_DATATYPES = 'DT_COMPLEX256'; + otherwise + extra.NIFTI_DATATYPES = 'DT_UNKNOWN'; + end + + switch hdr.dime.intent_code + case 2 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CORREL'; + case 3 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_TTEST'; + case 4 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_FTEST'; + case 5 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_ZSCORE'; + case 6 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CHISQ'; + case 7 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_BETA'; + case 8 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_BINOM'; + case 9 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_GAMMA'; + case 10 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_POISSON'; + case 11 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_NORMAL'; + case 12 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_FTEST_NONC'; + case 13 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CHISQ_NONC'; + case 14 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LOGISTIC'; + case 15 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LAPLACE'; + case 16 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_UNIFORM'; + case 17 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_TTEST_NONC'; + case 18 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_WEIBULL'; + case 19 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CHI'; + case 20 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_INVGAUSS'; + case 21 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_EXTVAL'; + case 22 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_PVAL'; + case 23 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LOGPVAL'; + case 24 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LOG10PVAL'; + case 1001 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_ESTIMATE'; + case 1002 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LABEL'; + case 1003 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_NEURONAME'; + case 1004 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_GENMATRIX'; + case 1005 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_SYMMATRIX'; + case 1006 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_DISPVECT'; + case 1007 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_VECTOR'; + case 1008 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_POINTSET'; + case 1009 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_TRIANGLE'; + case 1010 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_QUATERNION'; + case 1011 + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_DIMLESS'; + otherwise + extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_NONE'; + end + + extra.NIFTI_INTENT_NAMES = hdr.hist.intent_name; + + if hdr.hist.sform_code > 0 + switch hdr.hist.sform_code + case 1 + extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_SCANNER_ANAT'; + case 2 + extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_ALIGNED_ANAT'; + case 3 + extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_TALAIRACH'; + case 4 + extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_MNI_152'; + otherwise + 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'; + + switch hdr.hist.qform_code + case 1 + extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_SCANNER_ANAT'; + case 2 + extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_ALIGNED_ANAT'; + case 3 + extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_TALAIRACH'; + case 4 + 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_SPACE_UNIT = 'NIFTI_UNITS_METER'; + case 2 + extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_MM'; % millimeter + case 3 + extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_MICRO'; + otherwise + extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_UNKNOWN'; + end + + switch bitand(hdr.dime.xyzt_units, 56) % mask with 0x38 + case 8 + extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_SEC'; + case 16 + extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_MSEC'; + case 24 + extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_USEC'; % microsecond + otherwise + extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_UNKNOWN'; + end + + 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 + 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 + 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 + diff --git a/NIfTI_20140122/flip_lr.m b/NIfTI_20140122/flip_lr.m new file mode 100644 index 0000000..06543fd --- /dev/null +++ b/NIfTI_20140122/flip_lr.m @@ -0,0 +1,84 @@ +% When you load any ANALYZE or NIfTI file with 'load_nii.m', and view +% it with 'view_nii.m', you may find that the image is L-R flipped. +% This is because of the confusion of radiological and neurological +% convention in the medical image before NIfTI format is adopted. You +% can find more details from: +% +% http://www.rotman-baycrest.on.ca/~jimmy/UseANALYZE.htm +% +% Sometime, people even want to convert RAS (standard orientation) back +% to LAS orientation to satisfy the legend programs or processes. This +% program is only written for those purpose. So PLEASE BE VERY CAUTIOUS +% WHEN USING THIS 'FLIP_LR.M' PROGRAM. +% +% With 'flip_lr.m', you can convert any ANALYZE or NIfTI (no matter +% 3D or 4D) file to a flipped NIfTI file. This is implemented simply +% by flipping the affine matrix in the NIfTI header. Since the L-R +% orientation is determined there, so the image will be flipped. +% +% Usage: flip_lr(original_fn, flipped_fn, [old_RGB],[tolerance],[preferredForm]) +% +% original_fn - filename of the original ANALYZE or NIfTI (3D or 4D) file +% +% 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. +% +% 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. +% +% 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 +% +% Example: flip_lr('avg152T1_LR_nifti.nii', 'flipped_lr.nii'); +% flip_lr('avg152T1_RL_nifti.nii', 'flipped_rl.nii'); +% +% You will find that 'avg152T1_LR_nifti.nii' and 'avg152T1_RL_nifti.nii' +% are the same, and 'flipped_lr.nii' and 'flipped_rl.nii' are also the +% the same, but they are L-R flipped from 'avg152T1_*'. +% +% NIFTI data format can be found on: http://nifti.nimh.nih.gov +% +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) +% +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('old_RGB','var') | isempty(old_RGB) + old_RGB = 0; + end + + if ~exist('tolerance','var') | isempty(tolerance) + tolerance = 0.1; + 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 + diff --git a/NIfTI_20140122/get_nii_frame.m b/NIfTI_20140122/get_nii_frame.m new file mode 100644 index 0000000..154d526 --- /dev/null +++ b/NIfTI_20140122/get_nii_frame.m @@ -0,0 +1,164 @@ +% 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. +% +% 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. +% +% Returned values: +% +% total_scan - total number of image scans for the time frame +% +% NIFTI data format can be found on: http://nifti.nimh.nih.gov +% +% - 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); + 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); + + % Clean up after gunzip + % + if exist('gzFileName', 'var') + rmdir(tmpDir,'s'); + end + + return; % get_nii_frame + + +%--------------------------------------------------------------------- +function [ dsr ] = read_header(fid) + + fseek(fid,0,'bof'); + dsr.sizeof_hdr = fread(fid,1,'int32')'; % should be 348! + + fseek(fid,40,'bof'); + dsr.dim = fread(fid,8,'int16')'; + + return; % read_header + diff --git a/NIfTI_20140122/license.txt b/NIfTI_20140122/license.txt new file mode 100644 index 0000000..5a2d7f8 --- /dev/null +++ b/NIfTI_20140122/license.txt @@ -0,0 +1,24 @@ +Copyright (c) 2014, Jimmy Shen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/NIfTI_20140122/linpingxiangFA.hdr b/NIfTI_20140122/linpingxiangFA.hdr new file mode 100644 index 0000000..ed9c792 Binary files /dev/null and b/NIfTI_20140122/linpingxiangFA.hdr differ diff --git a/NIfTI_20140122/linpingxiangFA.img b/NIfTI_20140122/linpingxiangFA.img new file mode 100644 index 0000000..912a3bd Binary files /dev/null and b/NIfTI_20140122/linpingxiangFA.img differ diff --git a/NIfTI_20140122/load_nii.m b/NIfTI_20140122/load_nii.m new file mode 100644 index 0000000..c36ba77 --- /dev/null +++ b/NIfTI_20140122/load_nii.m @@ -0,0 +1,198 @@ +% Load NIFTI or ANALYZE dataset. Support both *.nii and *.hdr/*.img +% file extension. If file extension is not provided, *.hdr/*.img will +% be used as default. +% +% A subset of NIFTI transform is included. For non-orthogonal rotation, +% shearing etc., please use 'reslice_nii.m' to reslice the NIFTI file. +% It will not cause negative effect, as long as you remember not to do +% slice time correction after reslicing the NIFTI file. Output variable +% 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. +% +% 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. +% +% 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. +% +% 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. +% +% 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. +% +% 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. +% +% 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. +% +% 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 +% +% Returned values: +% +% nii structure: +% +% hdr - struct with NIFTI header fields. +% +% filetype - Analyze format .hdr/.img (0); +% NIFTI .hdr/.img (1); +% NIFTI .nii (2) +% +% fileprefix - NIFTI filename without extension. +% +% machine - machine string variable. +% +% img - 3D (or 4D) matrix of NIFTI data. +% +% 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 + diff --git a/NIfTI_20140122/load_nii_ext.m b/NIfTI_20140122/load_nii_ext.m new file mode 100644 index 0000000..56a2317 --- /dev/null +++ b/NIfTI_20140122/load_nii_ext.m @@ -0,0 +1,207 @@ +% Load NIFTI header extension after its header is loaded using load_nii_hdr. +% +% Usage: ext = load_nii_ext(filename) +% +% filename - NIFTI file name. +% +% Returned values: +% +% ext - Structure of NIFTI header extension, which includes num_ext, +% and all the extended header sections in the header extension. +% Each extended header section will have its esize, ecode, and +% edata, where edata can be plain text, xml, or any raw data +% that was saved in the extended header section. +% +% NIFTI data format can be found on: http://nifti.nimh.nih.gov +% +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) +% +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') + + 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 findstr('.nii',filename) & strcmp(filename(end-3:end), '.nii') + new_ext = 1; + filename(end-3:end)=''; + end + + if findstr('.hdr',filename) & strcmp(filename(end-3:end), '.hdr') + filename(end-3:end)=''; + end + + if findstr('.img',filename) & 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); + + 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 + + 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 + end + end + + + % Clean up after gunzip + % + if exist('gzFileName', 'var') + rmdir(tmpDir,'s'); + end + + + return % load_nii_ext + + +%--------------------------------------------------------------------- +function ext = read_extension(fid, vox_offset) + + ext = []; + + 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 isempty(ext) | ext.extension(1) == 0 + ext = []; + return; + end + + 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 + + ext.num_ext = length(ext.section); + + return % read_extension + diff --git a/NIfTI_20140122/load_nii_hdr.m b/NIfTI_20140122/load_nii_hdr.m new file mode 100644 index 0000000..01c83f3 --- /dev/null +++ b/NIfTI_20140122/load_nii_hdr.m @@ -0,0 +1,280 @@ +% internal function + +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) + +function [hdr, filetype, fileprefix, machine] = load_nii_hdr(fileprefix) + + if ~exist('fileprefix','var'), + error('Usage: [hdr, filetype, fileprefix, machine] = load_nii_hdr(filename)'); + end + + 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); + 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 + 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/NIfTI_20140122/load_nii_img.m b/NIfTI_20140122/load_nii_img.m new file mode 100644 index 0000000..60adb7d --- /dev/null +++ b/NIfTI_20140122/load_nii_img.m @@ -0,0 +1,392 @@ +% internal function + +% - 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} + fn = [fileprefix '.img']; + 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} + fseek(fid, 0, 'bof'); + 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 + + %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 + + % 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 + + % 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/NIfTI_20140122/load_untouch0_nii_hdr.m b/NIfTI_20140122/load_untouch0_nii_hdr.m new file mode 100644 index 0000000..297afaa --- /dev/null +++ b/NIfTI_20140122/load_untouch0_nii_hdr.m @@ -0,0 +1,200 @@ +% internal function + +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) + +function hdr = load_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 + diff --git a/NIfTI_20140122/load_untouch_header_only.m b/NIfTI_20140122/load_untouch_header_only.m new file mode 100644 index 0000000..8b68084 --- /dev/null +++ b/NIfTI_20140122/load_untouch_header_only.m @@ -0,0 +1,187 @@ +% Load NIfTI / Analyze header without applying any appropriate affine +% geometric transform or voxel intensity scaling. It is equivalent to +% 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). +% +% machine - a string, see below for details. The default here is 'ieee-le'. +% +% 'native' or 'n' - local machine format - the default +% 'ieee-le' or 'l' - IEEE floating point with little-endian +% byte ordering +% 'ieee-be' or 'b' - IEEE floating point with big-endian +% byte ordering +% 'vaxd' or 'd' - VAX D floating point and VAX ordering +% 'vaxg' or 'g' - VAX G floating point and VAX ordering +% 'cray' or 'c' - Cray floating point with big-endian +% byte ordering +% 'ieee-le.l64' or 'a' - IEEE floating point with little-endian +% byte ordering and 64 bit long data type +% 'ieee-be.l64' or 's' - IEEE floating point with big-endian byte +% ordering and 64 bit long data type. +% +% 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 [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 + diff --git a/NIfTI_20140122/load_untouch_nii.m b/NIfTI_20140122/load_untouch_nii.m new file mode 100644 index 0000000..067c4aa --- /dev/null +++ b/NIfTI_20140122/load_untouch_nii.m @@ -0,0 +1,191 @@ +% Load NIFTI or ANALYZE dataset, but not applying any appropriate affine +% geometric transform or voxel intensity scaling. +% +% Although according to NIFTI website, all those header information are +% supposed to be applied to the loaded NIFTI image, there are some +% situations that people do want to leave the original NIFTI header and +% data untouched. They will probably just use MATLAB to do certain image +% processing regardless of image orientation, and to save data back with +% the same NIfTI header. +% +% Since this program is only served for those situations, please use it +% together with "save_untouch_nii.m", and do not use "save_nii.m" or +% "view_nii.m" for the data that is loaded by "load_untouch_nii.m". For +% 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]) +% +% 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. +% +% 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. +% +% 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. +% +% 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. +% +% 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. +% +% 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. +% +% Returned values: +% +% nii structure: +% +% hdr - struct with NIFTI header fields. +% +% filetype - Analyze format .hdr/.img (0); +% NIFTI .hdr/.img (1); +% NIFTI .nii (2) +% +% fileprefix - NIFTI filename without extension. +% +% machine - machine string variable. +% +% 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 + diff --git a/NIfTI_20140122/load_untouch_nii_hdr.m b/NIfTI_20140122/load_untouch_nii_hdr.m new file mode 100644 index 0000000..a471536 --- /dev/null +++ b/NIfTI_20140122/load_untouch_nii_hdr.m @@ -0,0 +1,217 @@ +% internal function + +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) + +function hdr = load_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 + + 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/NIfTI_20140122/load_untouch_nii_img.m b/NIfTI_20140122/load_untouch_nii_img.m new file mode 100644 index 0000000..d9169a9 --- /dev/null +++ b/NIfTI_20140122/load_untouch_nii_img.m @@ -0,0 +1,468 @@ +% internal function + +% - 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} + fn = [fileprefix '.img']; + 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 + + 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} + fseek(fid, 0, 'bof'); + 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. + % + 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: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 + + % 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/NIfTI_20140122/make_ana.m b/NIfTI_20140122/make_ana.m new file mode 100644 index 0000000..883fc3e --- /dev/null +++ b/NIfTI_20140122/make_ana.m @@ -0,0 +1,210 @@ +% 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). +% +% 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. +% +% 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]. +% +% 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 ''. +% +% e.g.: +% origin = [33 44 13]; datatype = 64; +% ana = make_ana(img, [], origin, datatype); % default voxel_size +% +% ANALYZE 7.5 format: http://www.rotman-baycrest.on.ca/~jimmy/ANALYZE75.pdf +% +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) +% +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 = uint8(ana.img); + case 4 + ana.img = int16(ana.img); + case 8 + ana.img = int32(ana.img); + case 16 + ana.img = single(ana.img); + case 64 + ana.img = double(ana.img); + case 128 + ana.img = uint8(ana.img); + otherwise + error('Datatype is not supported by make_ana.'); + end + + return; % make_ana + + +%--------------------------------------------------------------------- +function hdr = make_header(dims, voxel_size, origin, datatype, ... + 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 + + +%--------------------------------------------------------------------- +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 + + +%--------------------------------------------------------------------- +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 + 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 + + +%--------------------------------------------------------------------- +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 + diff --git a/NIfTI_20140122/make_nii.m b/NIfTI_20140122/make_nii.m new file mode 100644 index 0000000..1af1c5e --- /dev/null +++ b/NIfTI_20140122/make_nii.m @@ -0,0 +1,256 @@ +% 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. +% +% 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]. +% +% 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 ''. +% +% e.g.: +% origin = [33 44 13]; datatype = 64; +% nii = make_nii(img, [], origin, datatype); % default voxel_size +% +% NIFTI data format can be found on: http://nifti.nimh.nih.gov +% +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) +% +function nii = make_nii(varargin) + + 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 = ''; + + 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 > 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(1) = dims(1) - 1; + dims = [dims 1]; + end + 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 + + maxval = round(double(max(nii.img(:)))); + minval = round(double(min(nii.img(:)))); + + nii.hdr = make_header(dims, voxel_size, origin, datatype, ... + descrip, maxval, minval); + + switch nii.hdr.dime.datatype + case 2 + nii.img = uint8(nii.img); + case 4 + nii.img = int16(nii.img); + case 8 + nii.img = int32(nii.img); + case 16 + nii.img = single(nii.img); + case 32 + nii.img = single(nii.img); + case 64 + nii.img = double(nii.img); + case 128 + nii.img = uint8(nii.img); + case 256 + nii.img = int8(nii.img); + case 511 + img = double(nii.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 + nii.img = uint16(nii.img); + case 768 + nii.img = uint32(nii.img); + case 1792 + nii.img = double(nii.img); + otherwise + error('Datatype is not supported by make_nii.'); + end + + return; % make_nii + + +%--------------------------------------------------------------------- +function hdr = make_header(dims, voxel_size, origin, datatype, ... + 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 + + +%--------------------------------------------------------------------- +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 + + +%--------------------------------------------------------------------- +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 + 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 + + +%--------------------------------------------------------------------- +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 + diff --git a/NIfTI_20140122/mat_into_hdr.m b/NIfTI_20140122/mat_into_hdr.m new file mode 100644 index 0000000..dc83a29 --- /dev/null +++ b/NIfTI_20140122/mat_into_hdr.m @@ -0,0 +1,83 @@ +%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). +% +% 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') +% + +% - 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); + + 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 ~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; + end + + 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; + + [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'; + + fid = fopen(hdr,'w',machine); + save_nii_hdr(h,fid); + fclose(fid); + end + end + + return; % mat_into_hdr + diff --git a/NIfTI_20140122/pad_nii.m b/NIfTI_20140122/pad_nii.m new file mode 100644 index 0000000..6398f22 --- /dev/null +++ b/NIfTI_20140122/pad_nii.m @@ -0,0 +1,142 @@ +% PAD_NII: Pad the NIfTI volume from any of the 6 sides +% +% Usage: nii = pad_nii(nii, [option]) +% +% Inputs: +% +% nii - NIfTI volume. +% +% 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] +% +% Options description in detail: +% ============================== +% +% 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_P: Number of voxels from Posterior 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_S: Number of voxels from Superior side will be padded. +% +% bg: Background intensity, which is 0 by default. +% +% NIfTI data format can be found on: http://nifti.nimh.nih.gov +% +% - Jimmy Shen (jshen@research.baycrest.org) +% +function nii = pad_nii(nii, opt) + + 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 + + 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 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 + end + + 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 + end + + 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 + end + + 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 + end + + 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 + 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,'bg') + bg = opt.bg; + end + end + + blk = bg * ones( pad_from_L, dims(2), dims(3) ); + nii.img = cat(1, blk, nii.img); + + blk = bg * ones( pad_from_R, dims(2), dims(3) ); + nii.img = cat(1, nii.img, blk); + + dims = size(nii.img); + + blk = bg * ones( dims(1), pad_from_P, dims(3) ); + nii.img = cat(2, blk, nii.img); + + blk = bg * ones( dims(1), pad_from_A, dims(3) ); + nii.img = cat(2, nii.img, blk); + + dims = size(nii.img); + + blk = bg * ones( dims(1), dims(2), pad_from_I ); + nii.img = cat(3, blk, nii.img); + + blk = bg * ones( dims(1), dims(2), pad_from_S ); + nii.img = cat(3, nii.img, blk); + + 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/NIfTI_20140122/reslice_nii.m b/NIfTI_20140122/reslice_nii.m new file mode 100644 index 0000000..2e43ad3 --- /dev/null +++ b/NIfTI_20140122/reslice_nii.m @@ -0,0 +1,321 @@ +% The basic application of the 'reslice_nii.m' program is to perform +% any 3D affine transform defined by a NIfTI format image. +% +% In addition, the 'reslice_nii.m' program can also be applied to +% generate an isotropic image from either a NIfTI format image or +% an ANALYZE format image. +% +% 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 +% "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]); +% +% old_fn - filename for original 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. +% +% 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. +% +% 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. +% +% 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 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 +% +% 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('img_idx','var') | isempty(img_idx) + img_idx = []; + end + + if ~exist('verbose','var') | isempty(verbose) + verbose = 1; + end + + if ~exist('preferredForm','var') | isempty(preferredForm) + preferredForm= 's'; % Jeff + end + + nii = load_nii_no_xform(old_fn, img_idx, 0, preferredForm); + + 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('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 ~exist('bg','var') | isempty(bg) + bg = mean([nii.img(1) nii.img(end)]); + end + + old_M = nii.hdr.hist.old_affine; + + 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 + + [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 ~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 + error('Incorrect quaternion values in this NIFTI data.'); + end + 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/NIfTI_20140122/rri_file_menu.m b/NIfTI_20140122/rri_file_menu.m new file mode 100644 index 0000000..ec54bf3 --- /dev/null +++ b/NIfTI_20140122/rri_file_menu.m @@ -0,0 +1,179 @@ +% Imbed a file menu to any figure. If file menu exist, it will append +% to the existing file menu. This file menu includes: Copy to clipboard, +% print, save, close etc. +% +% Usage: rri_file_menu(fig); +% +% rri_file_menu(fig,0) means no 'Close' menu. +% +% - 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'); + 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; + + return % copy_fig + + +%------------------------------------------------ +% +% 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'); + + if isempty(selected_file) | isempty(selected_path) + return; + end + + filename = [selected_path selected_file]; + + if(exist(filename,'file')==2) % file exist + + 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(strcmp(response,'No')) + return; + end + + end + + old_pointer = get(gcbf,'pointer'); + set(gcbf,'pointer','watch'); + + try + saveas(gcbf,filename); + catch + msg = 'ERROR: Cannot save file'; + set(findobj(gcf,'Tag','MessageLine'),'String',msg); + end + + set(gcbf,'pointer',old_pointer); + + return; % export_fig + diff --git a/NIfTI_20140122/rri_orient.m b/NIfTI_20140122/rri_orient.m new file mode 100644 index 0000000..9efcc3f --- /dev/null +++ b/NIfTI_20140122/rri_orient.m @@ -0,0 +1,106 @@ +% Convert image of different orientations to standard Analyze orientation +% +% 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]) + 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 + diff --git a/NIfTI_20140122/rri_orient_ui.m b/NIfTI_20140122/rri_orient_ui.m new file mode 100644 index 0000000..97cd1fb --- /dev/null +++ b/NIfTI_20140122/rri_orient_ui.m @@ -0,0 +1,251 @@ +% 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; +% e.g.: +% 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 + + orient = getappdata(gcf, 'orient'); + + if isempty(orient) + orient = [1 2 3]; + end + + if ishandle(orient_ui_fig) + close(gcf); + end + + return; + end + + action = varargin{1}; + + if strcmp(action, 'done') + click_done; + elseif strcmp(action, 'cancel') + uiresume; + end + + 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 + + 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 + + +%---------------------------------------------------------------------- +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'); + + orient = [x_orient y_orient z_orient]; + test_orient = [orient, orient + 3]; + test_orient = mod(test_orient, 3); + + if length(unique(test_orient)) ~= 3 + msgbox('Please don''t choose same or opposite direction','Error','modal'); + return; + end + + setappdata(gcf, 'orient', [x_orient y_orient z_orient]); + uiresume; + + return; % click_done + diff --git a/NIfTI_20140122/rri_select_file.m b/NIfTI_20140122/rri_select_file.m new file mode 100644 index 0000000..fe7d47e --- /dev/null +++ b/NIfTI_20140122/rri_select_file.m @@ -0,0 +1,636 @@ +function [selected_file, selected_path] = rri_select_file(varargin) +% +% USAGE: [selected_file, selected_path] = ... +% rri_select_file(dir_name, fig_title) +% +% Allow user to select a file from a list of Matlab competible +% file format +% +% Example: +% +% [selected_file, selected_path] = ... +% rri_select_file('/usr','Select Data File'); +% +% See Also RRI_GETFILES + +% -- Created June 2001 by Wilkin Chau, Rotman Research Institute +% +% use rri_select_file to open & save Matlab recognized format +% -- Modified Dec 2002 by Jimmy Shen, Rotman Research Institute +% + + if nargin == 0 | ischar(varargin{1}) % create rri_select_file figure + + dir_name = ''; + fig_title = 'Select a File'; + + if nargin > 0 + dir_name = varargin{1}; + end + + if nargin > 1 + fig_title = varargin{2}; + end + + Init(fig_title,dir_name); + uiwait; % wait for user finish + + selected_path = getappdata(gcf,'SelectedDirectory'); + selected_file = getappdata(gcf,'SelectedFile'); + + cd (getappdata(gcf,'StartDirectory')); + close(gcf); + return; + end; + + % clear the message line, + % + h = findobj(gcf,'Tag','MessageLine'); + set(h,'String',''); + + action = varargin{1}{1}; + + % change 'File format': + % update 'Files' & 'File selection' based on file pattern + % + if strcmp(action,'EditFilter'), + EditFilter; + + % run delete_fig when figure is closing + % + elseif strcmp(action,'delete_fig'), + delete_fig; + + % select 'Directories': + % go into the selected dir + % update 'Files' & 'File selection' based on file pattern + % + elseif strcmp(action,'select_dir'), + select_dir; + + % select 'Files': + % update 'File selection' + % + elseif strcmp(action,'select_file'), + select_file; + + % change 'File selection': + % if it is a file, select that, + % if it is more than a file (*), select those, + % if it is a directory, select based on file pattern + % + elseif strcmp(action,'EditSelection'), + EditSelection; + + % clicked 'Select' + % + elseif strcmp(action,'DONE_BUTTON_PRESSED'), + h = findobj(gcf,'Tag','SelectionEdit'); + [filepath,filename,fileext] = fileparts(get(h,'String')); + + if isempty(filepath) | isempty(filename) | isempty(fileext) + setappdata(gcf,'SelectedDirectory',[]); + setappdata(gcf,'SelectedFile',[]); + else + if ~strcmp(filepath(end),filesep) % not end with filesep + filepath = [filepath filesep]; % add a filesep to filepath + end + + setappdata(gcf,'SelectedDirectory',filepath); + setappdata(gcf,'SelectedFile',[filename fileext]); + end + + if getappdata(gcf,'ready') % ready to exit + uiresume; + end + + % clicked 'cancel' + % + elseif strcmp(action,'CANCEL_BUTTON_PRESSED'), + setappdata(gcf,'SelectedDirectory',[]); + setappdata(gcf,'SelectedFile',[]); + set(findobj(gcf,'Tag','FileList'),'String',''); + uiresume; + end; + + return; + + +% -------------------------------------------------------------------- +function Init(fig_title,dir_name), + + StartDirectory = pwd; + if isempty(StartDirectory), + StartDirectory = filesep; + end; + + filter_disp = {'JPEG image (*.jpg)', ... + 'TIFF image, compressed (*.tif)', ... + 'EPS Level 1 (*.eps)', ... + 'Adobe Illustrator 88 (*.ai)', ... + 'Enhanced metafile (*.emf)', ... + 'Matlab Figure (*.fig)', ... + 'Matlab M-file (*.m)', ... + 'Portable bitmap (*.pbm)', ... + 'Paintbrush 24-bit (*.pcx)', ... + 'Portable Graymap (*.pgm)', ... + 'Portable Network Graphics (*.png)', ... + 'Portable Pixmap (*.ppm)', ... + }; + + filter_string = {'*.jpg', ... + '*.tif', ... + '*.eps', ... + '*.ai', ... + '*.emf', ... + '*.fig', ... + '*.m', ... + '*.pbm', ... + '*.pcx', ... + '*.pgm', ... + '*.png', ... + '*.ppm', ... + }; + +% filter_disp = char(filter_disp); + filter_string = char(filter_string); + + margine = 0.05; + line_height = 0.07; + char_height = line_height*0.8; + + save_setting_status = 'on'; + rri_select_file_pos = []; + + try + load('pls_profile'); + catch + end + + if ~isempty(rri_select_file_pos) & strcmp(save_setting_status,'on') + + pos = rri_select_file_pos; + + else + + w = 0.4; + h = 0.6; + x = (1-w)/2; + y = (1-h)/2; + + pos = [x y w h]; + + end + + h0 = figure('parent',0, 'Color',[0.8 0.8 0.8], ... + 'Units','normal', ... + 'Name',fig_title, ... + 'NumberTitle','off', ... + 'MenuBar','none', ... + 'Position', pos, ... + 'deleteFcn','rri_select_file({''delete_fig''});', ... + 'WindowStyle', 'modal', ... + 'Tag','GetFilesFigure', ... + 'ToolBar','none'); + + x = margine; + y = 1 - 1*line_height - margine; + w = 1-2*x; + h = char_height; + + pos = [x y w h]; + + h1 = uicontrol('Parent',h0, ... % Filter Label + 'Style','text', ... + 'Units','normal', ... + 'BackgroundColor',[0.8 0.8 0.8], ... + 'fontunit','normal', ... + 'FontSize',0.5, ... + 'HorizontalAlignment','left', ... + 'Position', pos, ... + 'String','Choose one of the file format:', ... + 'Tag','FilterLabel'); + + y = 1 - 2*line_height - margine + line_height*0.2; + w = 1-2*x; + + pos = [x y w h]; + + h_filter = uicontrol('Parent',h0, ... % Filter list + 'Style','popupmenu', ... + 'Units','normal', ... + 'BackgroundColor',[1 1 1], ... + 'fontunit','normal', ... + 'FontSize',0.5, ... + 'HorizontalAlignment','left', ... + 'Position', pos, ... + 'String', filter_disp, ... + 'user', filter_string, ... + 'value', 1, ... + 'Callback','rri_select_file({''EditFilter''});', ... + 'Tag','FilterEdit'); + + y = 1 - 3*line_height - margine; + w = 0.5 - x - margine/2; + + pos = [x y w h]; + + h1 = uicontrol('Parent',h0, ... % Directory Label + 'Style','text', ... + 'Units','normal', ... + 'BackgroundColor',[0.8 0.8 0.8], ... + 'fontunit','normal', ... + 'FontSize',0.5, ... + 'HorizontalAlignment','left', ... + 'ListboxTop',0, ... + 'Position', pos, ... + 'String','Directories', ... + 'Tag','DirectoryLabel'); + + x = 0.5; + y = 1 - 3*line_height - margine; + w = 0.5 - margine; + + pos = [x y w h]; + + h1 = uicontrol('Parent',h0, ... % File Label + 'Style','text', ... + 'Units','normal', ... + 'BackgroundColor',[0.8 0.8 0.8], ... + 'fontunit','normal', ... + 'FontSize',0.5, ... + 'HorizontalAlignment','left', ... + 'ListboxTop',0, ... + 'Position', pos, ... + 'String','Files', ... + 'Tag','FileLabel'); + + x = margine; + y = 4*line_height + margine; + w = 0.5 - x - margine/2; + h = 1 - 7*line_height - 2*margine; + + pos = [x y w h]; + + h_dir = uicontrol('Parent',h0, ... % Directory Listbox + 'Style','listbox', ... + 'Units','normal', ... + 'fontunit','normal', ... + 'FontSize',0.08, ... + 'HorizontalAlignment','left', ... + 'Interruptible', 'off', ... + 'ListboxTop',1, ... + 'Position', pos, ... + 'String', '', ... + 'Callback','rri_select_file({''select_dir''});', ... + 'Tag','DirectoryList'); + + x = 0.5; + y = 4*line_height + margine; + w = 0.5 - margine; + h = 1 - 7*line_height - 2*margine; + + pos = [x y w h]; + + h_file = uicontrol('Parent',h0, ... % File Listbox + 'Style','listbox', ... + 'Units','normal', ... + 'fontunit','normal', ... + 'FontSize',0.08, ... + 'HorizontalAlignment','left', ... + 'ListboxTop',1, ... + 'Position', pos, ... + 'String', '', ... + 'Callback','rri_select_file({''select_file''});', ... + 'Tag','FileList'); + + x = margine; + y = 3*line_height + margine - line_height*0.2; + w = 1-2*x; + h = char_height; + + pos = [x y w h]; + + h1 = uicontrol('Parent',h0, ... % Selection Label + 'Style','text', ... + 'Units','normal', ... + 'BackgroundColor',[0.8 0.8 0.8], ... + 'fontunit','normal', ... + 'FontSize',0.5, ... + 'HorizontalAlignment','left', ... + 'Position', pos, ... + 'String','File you selected:', ... + 'Tag','SelectionLabel'); + + y = 2*line_height + margine; + w = 1-2*x; + + pos = [x y w h]; + + h_select = uicontrol('Parent',h0, ... % Selection Edit + 'Style','edit', ... + 'Units','normal', ... + 'BackgroundColor',[1 1 1], ... + 'fontunit','normal', ... + 'FontSize',0.5, ... + 'HorizontalAlignment','left', ... + 'Position', pos, ... + 'String', '', ... + 'Callback','rri_select_file({''EditSelection''});', ... + 'Tag','SelectionEdit'); + + x = 2*margine; + y = line_height/2 + margine; + w = 0.2; + h = line_height; + + pos = [x y w h]; + + h_done = uicontrol('Parent',h0, ... % DONE + 'Units','normal', ... + 'fontunit','normal', ... + 'FontSize',0.5, ... + 'ListboxTop',0, ... + 'Position', pos, ... + 'HorizontalAlignment','center', ... + 'String','Save', ... % 'Select', ... + 'Callback','rri_select_file({''DONE_BUTTON_PRESSED''});', ... + 'Tag','DONEButton'); + + x = 1 - x - w; + + pos = [x y w h]; + + h_cancel = uicontrol('Parent',h0, ... % CANCEL + 'Units','normal', ... + 'fontunit','normal', ... + 'FontSize',0.5, ... + 'ListboxTop',0, ... + 'Position', pos, ... + 'HorizontalAlignment','center', ... + 'String','Cancel', ... + 'Callback','rri_select_file({''CANCEL_BUTTON_PRESSED''});', ... + 'Tag','CANCELButton'); + + if isempty(dir_name) + dir_name = StartDirectory; + end + + set(h_select,'string',dir_name); + + filter_select = get(h_filter,'value'); + filter_pattern = filter_string(filter_select,:); + + setappdata(gcf,'FilterPattern',deblank(filter_pattern)); + setappdata(gcf,'filter_string',filter_string); + + setappdata(gcf,'h_filter', h_filter); + setappdata(gcf,'h_dir', h_dir); + setappdata(gcf,'h_file', h_file); + setappdata(gcf,'h_select', h_select); + setappdata(gcf,'h_done', h_done); + setappdata(gcf,'h_cancel', h_cancel); + setappdata(gcf,'StartDirectory',StartDirectory); + + EditSelection; + + h_file = getappdata(gcf,'h_file'); + if isempty(get(h_file,'string')) + setappdata(gcf,'ready',0); + else + setappdata(gcf,'ready',1); + end + + return; % Init + + +% called by all the actions, to update 'Directories' or 'Files' +% based on filter_pattern. Select first file in filelist. +% +% -------------------------------------------------------------------- + +function update_dirlist; + + filter_path = getappdata(gcf,'curr_dir'); + filter_pattern = getappdata(gcf,'FilterPattern'); + + if exist(filter_pattern) == 2 % user input specific filename + is_single_file = 1; % need manually take path out later + else + is_single_file = 0; + end + + % take the file path out from filter_pattern + % + [fpath fname fext] = fileparts(filter_pattern); + filter_pattern = [fname fext]; + + dir_struct = dir(filter_path); + if isempty(dir_struct) + msg = 'ERROR: Directory not found!'; + uiwait(msgbox(msg,'File Selection Error','modal')); + return; + end; + + old_pointer = get(gcf,'Pointer'); + set(gcf,'Pointer','watch'); + + dir_list = dir_struct(find([dir_struct.isdir] == 1)); + [sorted_dir_names,sorted_dir_index] = sortrows({dir_list.name}'); + + dir_struct = dir([filter_path filesep filter_pattern]); + if isempty(dir_struct) + sorted_file_names = []; + else + file_list = dir_struct(find([dir_struct.isdir] == 0)); + + if is_single_file % take out path + tmp = file_list.name; + [fpath fname fext] = fileparts(tmp); + file_list.name = [fname fext]; + end + + [sorted_file_names,sorted_file_index] = sortrows({file_list.name}'); + end; + + disp_dir_names = []; % if need full path, use this + % instead of sorted_dir_names + for i=1:length(sorted_dir_names) + tmp = [filter_path filesep sorted_dir_names{i}]; + disp_dir_names = [disp_dir_names {tmp}]; + end + + h = findobj(gcf,'Tag','DirectoryList'); + set(h,'String',sorted_dir_names,'Value',1); + + h = findobj(gcf,'Tag','FileList'); + set(h,'String',sorted_file_names,'value',1); + + h_select = getappdata(gcf,'h_select'); + if strcmp(filter_path(end),filesep) % filepath end with filesep + filter_path = filter_path(1:end-1); % take filesep out + end + + if isempty(sorted_file_names) + set(h_select,'string',[filter_path filesep]); + else + set(h_select,'string',[filter_path filesep sorted_file_names{1}]); + end + + set(gcf,'Pointer',old_pointer); + + return; % update_dirlist + + +% change 'File format': +% update 'Files' & 'File selection' based on file pattern +% +% -------------------------------------------------------------------- + +function EditFilter() + + filter_select = get(gcbo,'value'); + filter_string = getappdata(gcf,'filter_string'); + filter_pattern = filter_string(filter_select,:); + filter_path = getappdata(gcf,'curr_dir'); + + % update filter_pattern + setappdata(gcf,'FilterPattern',deblank(filter_pattern)); + + if isempty(filter_path), + filter_path = filesep; + end; + + update_dirlist; + + h_file = getappdata(gcf,'h_file'); + if isempty(get(h_file,'string')) + setappdata(gcf,'ready',0); + else + setappdata(gcf,'ready',1); + end + + return; % EditFilter + + +% select 'Directories': +% go into the selected dir +% update 'Files' & 'File selection' based on file pattern +% +% -------------------------------------------------------------------- + +function select_dir() + + listed_dir = get(gcbo,'String'); + selected_dir_idx = get(gcbo,'Value'); + selected_dir = listed_dir{selected_dir_idx}; + curr_dir = getappdata(gcf,'curr_dir'); + + % update the selection box + % + try + cd ([curr_dir filesep selected_dir]); + catch + msg = 'ERROR: Cannot access directory'; + uiwait(msgbox(msg,'File Selection Error','modal')); + return; + end; + + if isempty(pwd) + curr_dir = filesep; + else + curr_dir = pwd; + end; + + setappdata(gcf,'curr_dir',curr_dir); + update_dirlist; + + h_file = getappdata(gcf,'h_file'); + if isempty(get(h_file,'string')) + setappdata(gcf,'ready',0); + else + setappdata(gcf,'ready',1); + end + + return; % select_dir + + +% select 'Files': +% update 'File selection' +% +% -------------------------------------------------------------------- + +function select_file() + + setappdata(gcf,'ready',1); + listed_file = get(gcbo,'String'); + selected_file_idx = get(gcbo,'Value'); + selected_file = listed_file{selected_file_idx}; + curr_dir = getappdata(gcf,'curr_dir'); + + if strcmp(curr_dir(end),filesep) % filepath end with filesep + curr_dir = curr_dir(1:end-1); % take filesep out + end + + h_select = getappdata(gcf,'h_select'); + set(h_select,'string',[curr_dir filesep selected_file]); + + return; % select_file + + +% change 'File selection': +% if it is a file, select that, +% if it is more than a file (*), select those, +% if it is a directory, select based on file pattern +% +% -------------------------------------------------------------------- + +function EditSelection() + + filter_string = getappdata(gcf,'filter_string'); + h_select = getappdata(gcf,'h_select'); + selected_file = get(h_select,'string'); + + if exist(selected_file) == 7 % if user enter a dir + setappdata(gcf,'ready',0); + setappdata(gcf,'curr_dir',selected_file); % get new dir + update_dirlist; + else + + setappdata(gcf,'ready',1); + + [fpath fname fext]= fileparts(selected_file); + if exist(fpath) ~=7 % fpath is not a dir + setappdata(gcf,'ready',0); + msg = 'ERROR: Cannot access directory'; + uiwait(msgbox(msg,'File Selection Error','modal')); + end + + % if the file format user entered is not supported by matlab + if isempty(strmatch(['*',fext],filter_string,'exact')) + setappdata(gcf,'ready',0); + msg = 'ERROR: File format is not supported by Matlab.'; + uiwait(msgbox(msg,'File Selection Error','modal')); + end + + end + + return; % EditSelection + + +% -------------------------------------------------------------------- + +function delete_fig() + + try + load('pls_profile'); + pls_profile = which('pls_profile.mat'); + + rri_select_file_pos = get(gcbf,'position'); + + save(pls_profile, '-append', 'rri_select_file_pos'); + catch + end + + return; + diff --git a/NIfTI_20140122/rri_xhair.m b/NIfTI_20140122/rri_xhair.m new file mode 100644 index 0000000..9a9951a --- /dev/null +++ b/NIfTI_20140122/rri_xhair.m @@ -0,0 +1,92 @@ +% rri_xhair: create a pair of full_cross_hair at point [x y] in +% axes h_ax, and return xhair struct +% +% Usage: xhair = rri_xhair([x y], xhair, h_ax); +% +% If omit xhair, rri_xhair will create a pair of xhair; otherwise, +% rri_xhair will update the xhair. If omit h_ax, current axes will +% be used. +% + +% 24-nov-2003 jimmy (jimmy@rotman-baycrest.on.ca) +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +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; + 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/NIfTI_20140122/rri_zoom_menu.m b/NIfTI_20140122/rri_zoom_menu.m new file mode 100644 index 0000000..e1c9c6c --- /dev/null +++ b/NIfTI_20140122/rri_zoom_menu.m @@ -0,0 +1,33 @@ +% Imbed a zoom menu to any figure. +% +% Usage: rri_zoom_menu(fig); +% + +% - 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'');'); + + return; + end + + 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 + diff --git a/NIfTI_20140122/save_nii.m b/NIfTI_20140122/save_nii.m new file mode 100644 index 0000000..b2d0dd4 --- /dev/null +++ b/NIfTI_20140122/save_nii.m @@ -0,0 +1,286 @@ +% 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 +% 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 +% 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 +% 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 +% 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 +% +% 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) +% +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 + + +%----------------------------------------------------------------------------------- +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 + 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 + diff --git a/NIfTI_20140122/save_nii_ext.m b/NIfTI_20140122/save_nii_ext.m new file mode 100644 index 0000000..5e1509c --- /dev/null +++ b/NIfTI_20140122/save_nii_ext.m @@ -0,0 +1,38 @@ +% Save NIFTI header extension. +% +% Usage: save_nii_ext(ext, fid) +% +% ext - struct with NIFTI header extension fields. +% +% NIFTI data format can be found on: http://nifti.nimh.nih.gov +% +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) +% +function save_nii_ext(ext, fid) + + 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 + + write_ext(ext, fid); + + 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 + + return; % write_ext + diff --git a/NIfTI_20140122/save_nii_hdr.m b/NIfTI_20140122/save_nii_hdr.m new file mode 100644 index 0000000..225f297 --- /dev/null +++ b/NIfTI_20140122/save_nii_hdr.m @@ -0,0 +1,227 @@ +% internal function + +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) + +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 + + +%--------------------------------------------------------------------- +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 + + +%--------------------------------------------------------------------- +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 + + +%--------------------------------------------------------------------- +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 + + +%--------------------------------------------------------------------- +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 + diff --git a/NIfTI_20140122/save_untouch0_nii_hdr.m b/NIfTI_20140122/save_untouch0_nii_hdr.m new file mode 100644 index 0000000..ba1f322 --- /dev/null +++ b/NIfTI_20140122/save_untouch0_nii_hdr.m @@ -0,0 +1,219 @@ +% internal function + +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) + +function save_nii_hdr(hdr, fid) + + if ~isequal(hdr.hk.sizeof_hdr,348), + error('hdr.hk.sizeof_hdr must be 348.'); + 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*/ + + 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 + + +%--------------------------------------------------------------------- +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 + + +%--------------------------------------------------------------------- +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 + diff --git a/NIfTI_20140122/save_untouch_header_only.m b/NIfTI_20140122/save_untouch_header_only.m new file mode 100644 index 0000000..b145058 --- /dev/null +++ b/NIfTI_20140122/save_untouch_header_only.m @@ -0,0 +1,71 @@ +% 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 +% 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; + + % 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), '.hdr.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_hdr(hdr, fileprefix); + + % 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 + + fclose(fid); + + return % write_hdr + diff --git a/NIfTI_20140122/save_untouch_nii.m b/NIfTI_20140122/save_untouch_nii.m new file mode 100644 index 0000000..8002172 --- /dev/null +++ b/NIfTI_20140122/save_untouch_nii.m @@ -0,0 +1,232 @@ +% Save NIFTI or ANALYZE dataset that is loaded by "load_untouch_nii.m". +% The output image format and file extension will be the same as the +% input one (NIFTI.nii, NIFTI.img or ANALYZE.img). Therefore, any file +% 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. +% +% - 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 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=[]; + % save(fileprefix, 'M'); + %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 + 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 + diff --git a/NIfTI_20140122/save_untouch_nii_hdr.m b/NIfTI_20140122/save_untouch_nii_hdr.m new file mode 100644 index 0000000..33fa9ed --- /dev/null +++ b/NIfTI_20140122/save_untouch_nii_hdr.m @@ -0,0 +1,207 @@ +% internal function + +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) + +function save_nii_hdr(hdr, fid) + + if ~isequal(hdr.hk.sizeof_hdr,348), + error('hdr.hk.sizeof_hdr must be 348.'); + 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*/ + + 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 + + +%--------------------------------------------------------------------- +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 + + +%--------------------------------------------------------------------- +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 + diff --git a/NIfTI_20140122/save_untouch_slice.m b/NIfTI_20140122/save_untouch_slice.m new file mode 100644 index 0000000..af73521 --- /dev/null +++ b/NIfTI_20140122/save_untouch_slice.m @@ -0,0 +1,580 @@ +% Save back to the original image with a portion of slices that was +% loaded by "load_untouch_nii". You can process those slices matrix +% 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 - 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) +% +% 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. +% +% 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. +% +% 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. +% +% 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); +% +% - 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('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('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)) + 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)]; + 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)]; + 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)]; + 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 + 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} + fseek(fid, 0, 'bof'); + 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 + + 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)); + + % 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 + + fclose(fid); + + return % write_image + diff --git a/NIfTI_20140122/unxform_nii.m b/NIfTI_20140122/unxform_nii.m new file mode 100644 index 0000000..f98ef32 --- /dev/null +++ b/NIfTI_20140122/unxform_nii.m @@ -0,0 +1,40 @@ +% 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 +% in xform_nii. +% +% Usage: a = load_nii('original_name'); +% manipulate a.img to make array b; +% +% if you use unxform_nii to un-tranform the image (img) data +% block, then nii.original.hdr is the corresponding header. +% +% nii.original.img = unxform_nii(a, b); +% save_nii(nii.original,'newname'); +% +% Where, 'newname' is created with data in the same space as the +% 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); + + for i = 1:3 + if flip_orient(i) + outblock = flipdim(outblock, i); + end + end + end; + + return; + diff --git a/NIfTI_20140122/verify_nii_ext.m b/NIfTI_20140122/verify_nii_ext.m new file mode 100644 index 0000000..c904df4 --- /dev/null +++ b/NIfTI_20140122/verify_nii_ext.m @@ -0,0 +1,45 @@ +% Verify NIFTI header extension to make sure that each extension section +% must be an integer multiple of 16 byte long that includes the first 8 +% bytes of esize and ecode. If the length of extension section is not the +% above mentioned case, edata should be padded with all 0. +% +% Usage: [ext, esize_total] = verify_nii_ext(ext) +% +% ext - Structure of NIFTI header extension, which includes num_ext, +% and all the extended header sections in the header extension. +% Each extended header section will have its esize, ecode, and +% edata, where edata can be plain text, xml, or any raw data +% that was saved in the extended header section. +% +% esize_total - Sum of all esize variable in all header sections. +% +% NIFTI data format can be found on: http://nifti.nimh.nih.gov +% +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) +% +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; + + for i=1:ext.num_ext + if ~isfield(ext.section(i), 'ecode') | ~isfield(ext.section(i), 'edata') + error('Incorrect NIFTI header extension structure.'); + end + + 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/NIfTI_20140122/view_nii.m b/NIfTI_20140122/view_nii.m new file mode 100644 index 0000000..45e26b5 --- /dev/null +++ b/NIfTI_20140122/view_nii.m @@ -0,0 +1,4873 @@ +% 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]) +% +% 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: +% +% 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.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: +% ============================== +% +% 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. +% +% 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 +% +% 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. +% +% +% 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. +% +% 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. +% +% 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. +% +% +% 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. +% +% 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. +% +% 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. +% +% 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. +% +% 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. +% +% 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. +% +% 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'. +% +% +% 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] +% +% +% e.g.: +% nii = load_nii('T1'); % T1.img/hdr +% view_nii(nii); +% +% 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); +% +% +% 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 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 == 2 + if ~isstruct(varargin{2}) + error('2nd parameter should be either nii struct or option struct'); + end + + 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') ) + + error('Option here cannot contain "init", "updatenii", or "updateimg" comand'); + end + end + + if nargin == 3 + nii = varargin{2}; + opt = varargin{3}; + + 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(nii,'untouch') & nii.untouch == 1 + error('Usage: please use ''load_nii.m'' to load the structure.'); + end + end + end + + set(fig, 'menubar', 'none'); + + elseif ischar(varargin{1}) % call back by event + + command = lower(varargin{1}); + fig = gcbf; + + else % start nii with a new figure + + 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 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 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); + + end + + if ~isempty(opt) + + if isfield(opt,'command') + command = lower(opt.command); + end + + if isempty(command) + command = 'update'; + end + + if isfield(opt,'usecolorbar') + usecolorbar = opt.usecolorbar; + end + + if isfield(opt,'usepanel') + usepanel = opt.usepanel; + end + + if isfield(opt,'usecrosshair') + usecrosshair = opt.usecrosshair; + end + + if isfield(opt,'usestretch') + usestretch = opt.usestretch; + end + + if isfield(opt,'useimagesc') + useimagesc = opt.useimagesc; + end + + if isfield(opt,'useinterp') + useinterp = opt.useinterp; + end + + if isfield(opt,'setarea') + setarea = opt.setarea; + end + + if isfield(opt,'setunit') + setunit = opt.setunit; + end + + if isfield(opt,'setviewpoint') + setviewpoint = opt.setviewpoint; + end + + if isfield(opt,'setscanid') + setscanid = opt.setscanid; + end + + 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 + end + + 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 + end + + 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 + end + + 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 + end + + 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 + end + + 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 + 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 ~strcmpi(class(opt.setvalue.val),'single') + opt.setvalue.val = single(opt.setvalue.val); + 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 + + case {'init'} + + 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); + + % get status + % + status = get_status(fig); + + case {'update'} + + nii_view = getappdata(fig,'nii_view'); + h = fig; + + if isempty(nii_view) + 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); + + end + + % get status + % + status = get_status(h); + + case {'updateimg'} + + 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); + end + + nii_view = getappdata(fig,'nii_view'); + + if isempty(nii_view) + error('The figure should already contain a 3-View plot.'); + end + + img = nii; + update_img(img, fig, opt); + + % get status + % + status = get_status(fig); + + case {'updatenii'} + + nii_view = getappdata(fig,'nii_view'); + + if isempty(nii_view) + 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'); + end + + if isfield(nii,'untouch') & nii.untouch == 1 + error('Usage: please use ''load_nii.m'' to load the structure.'); + end + + opt.command = 'clearnii'; + view_nii(fig, opt); + + opt.command = 'init'; + view_nii(fig, nii, opt); + + % get status + % + status = get_status(fig); + + case {'clearnii'} + + 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 + end + + 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'} + + 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; + end + + nii_view = getappdata(fig,'nii_view'); + nii_view = get_slice_position(nii_view,view); + + if isfield(nii_view, 'disp') + img = nii_view.disp; + else + 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; + + update_nii_view(nii_view); + + if ~isempty(nii_view.buttondown) + eval(nii_view.buttondown); + end + + 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; + end + + nii_view = getappdata(fig,'nii_view'); + nii_view = get_slider_position(nii_view); + + if isfield(nii_view, 'disp') + img = nii_view.disp; + else + 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 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); + end + + case {'impos_edit'} + + nii_view = getappdata(fig,'nii_view'); + impos = str2num(get(nii_view.handles.impos,'string')); + + if isfield(nii_view, 'disp') + img = nii_view.disp; + else + 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; + end + + slices.sag = round(impos(1)); + slices.cor = round(impos(2)); + slices.axi = round(impos(3)); + + 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)); + end + + 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)); + 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)'); + 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 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 + + 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 + + axes(nii_view.handles.axial_axes); + axes(nii_view.handles.coronal_axes); + axes(nii_view.handles.sagittal_axes); + + if ~isempty(nii_view.buttondown) + eval(nii_view.buttondown); + end + + case 'coordinates', + + nii_view = getappdata(fig,'nii_view'); + set_image_value(nii_view); + + case 'crosshair', + + 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'); + 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'); + end + + case 'xhair_color', + + old_color = get(gcbo,'user'); + new_color = uisetcolor(old_color); + update_crosshaircolor(fig, new_color); + + case {'color','contrast_def'} + + 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 + end + + [custom_color_map, custom_colorindex] = change_colormap(fig); + + if strcmpi(command, 'color') + + 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 isfield(nii_view, 'highcolor') + sethighcolor = nii_view.highcolor; + else + sethighcolor = []; + end + + 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 + + update_enable(fig, contrastopt); + + end + + case {'neg_color','brightness','contrast'} + + change_colormap(fig); + + case {'brightness_def'} + + nii_view = getappdata(fig,'nii_view'); + set(nii_view.handles.brightness,'value',0); + change_colormap(fig); + + case 'hist_plot' + + hist_plot(fig); + + case 'hist_eq' + + hist_eq(fig); + + case 'move_cursor' + + move_cursor(fig); + + case 'edit_change_scan' + + change_scan('edit_change_scan'); + + case 'slider_change_scan' + + change_scan('slider_change_scan'); + + 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 + + if isempty(area) + area = [0.05 0.05 0.9 0.9]; + end + + if isempty(setscanid) + setscanid = 1; + else + setscanid = round(setscanid); + + if setscanid < 1 + setscanid = 1; + 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 isempty(usepanel) + usepanel = 1; + end + + if isempty(usestretch) + usestretch = 1; + end + + if isempty(useimagesc) + useimagesc = 1; + end + + if isempty(useinterp) + useinterp = 0; + end + + 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 + + bgimg = double(nii.img); + minbg = double(min(bgimg(:))); + maxbg = double(max(bgimg(:))); + + bgimg = scale_in(bgimg, minbg, maxbg, 55) + 200; % scale to 201~256 + + % 56 level for brain structure + % +% highcolor = [zeros(1,3);gray(55)]; + highcolor = gray(56); + cbarminmax = [minvalue maxvalue]; + + 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 + + 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 + + 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; + + 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 + set(handles.Tcoord, 'visible', 'on'); + end + + x = x + w + 0.005; + w = info_pos(3)*0.5 - 0.005; + + pos = [x y w h]; + + 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'');'); + +% 'TooltipString','Choose Voxel, MNI or Talairach Coordinates',... +% 'String',{'Voxel','MNI (mm)','Talairach (mm)'},... + + Font.FontSize = 12; + + 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 + + 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,'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,'axial_slider') & ishandle(handles.axial_slider) + set(handles.axial_slider, 'visible', 'off'); + end + end + + [top1_label, top2_label, side1_label, side2_label] = ... + dir_label(fig, top_ax, front_ax, side_ax); + + % store label handles + % + handles.top1_label = top1_label; + handles.top2_label = top2_label; + handles.side1_label = side1_label; + handles.side2_label = side2_label; + + % plot colorbar + % + if ~isempty(cbar_axes) & ~isempty(cbarminmax_axes) + +if 0 + if isempty(color_map) + level = colorlevel + num_highcolor; + else + level = size([color_map; highcolor], 1); + end +end + + if isempty(color_map) + level = colorlevel; + else + level = size([color_map], 1); + end + + niiclass = class(nii.img); + + 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; + + end + + nii_view.handles = handles; % store handles + + 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 + + set_coordinates(nii_view,useinterp); % coord unit + + if ~isfield(nii_view, 'axi_xhair') | ... + ~isfield(nii_view, 'cor_xhair') | ... + ~isfield(nii_view, 'sag_xhair') + + nii_view.axi_xhair = []; % top cross hair + nii_view.cor_xhair = []; % front cross hair + nii_view.sag_xhair = []; % side cross hair + + end + + if ~isempty(color_map) + nii_view.color_map = color_map; + end + + if ~isempty(colorlevel) + nii_view.colorlevel = colorlevel; + end + + if ~isempty(highcolor) + nii_view.highcolor = highcolor; + end + + update_nii_view(nii_view); + + if ~isempty(setunit) + update_unit(fig, setunit); + end + + if ~isempty(setviewpoint) + update_viewpoint(fig, setviewpoint); + end + + if ~isempty(setcrosshaircolor) + update_crosshaircolor(fig, setcrosshaircolor); + end + + if ~isempty(usecrosshair) + update_usecrosshair(fig, usecrosshair); + end + + nii_menu = getappdata(fig, 'nii_menu'); + + 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 + + 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_view = getappdata(fig,'nii_view'); + change_interp = 0; + + if isfield(opt, 'useinterp') & opt.useinterp ~= nii_view.useinterp + nii_view.useinterp = opt.useinterp; + change_interp = 1; + end + + setscanid = 1; + + if isfield(opt, 'setscanid') + setscanid = round(opt.setscanid); + + if setscanid < 1 + setscanid = 1; + end + + 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 = double(min(setvalue.val)); + maxvalue = double(max(setvalue.val)); + end + + bgimg = double(img); + minbg = double(min(bgimg(:))); + maxbg = double(max(bgimg(:))); + + bgimg = scale_in(bgimg, minbg, maxbg, 55) + 200; % scale to 201~256 + + cbarminmax = [minvalue maxvalue]; + + if nii_view.useinterp + + % scale signal data to 1~200 + % + img = repmat(nan, size(img)); + img(setvalue.idx) = setvalue.val; + + % 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 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 + % + 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); + + minvalue = double(min(img(:))); + maxvalue = double(max(img(:))); + + if isfield(opt,'glblocminmax') & ~isempty(opt.glblocminmax) + minvalue = opt.glblocminmax(1); + end + + 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 + + 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 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); + 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,:)); + + 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 + + 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 + + set(nii_view.handles.coronal_image,'CData',double(Scor)); + end + + set(nii_view.handles.coronal_axes,'CLim',clim); + + if ~isempty(nii_view.bgimg) + Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag,:,:)); + + 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,'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 + + 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]); + 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) + + set(fig,'unit','pixel'); + + 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 + + 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' + + 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 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 + + 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'); + + return; % axes_pos + + +%---------------------------------------------------------------- +function [top_ax, front_ax, side_ax] ... + = create_ax(fig, area, vol_size, usestretch) + + cur_fig = gcf; % save h_wait fig + figure(fig); + + [top_pos, front_pos, side_pos] = ... + axes_pos(fig,area,vol_size,usestretch); + + nii_view = getappdata(fig, 'nii_view'); + + 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(top_ax, 'position', top_pos); + set(front_ax, 'position', front_pos); + set(side_ax, 'position', side_pos); + end + + figure(cur_fig); + + return; % create_ax + + +%---------------------------------------------------------------- +function [cbar_axes, cbarminmax_axes] = create_cbar_axes(fig, cbar_area, nii_view) + + if isempty(cbar_area) % without_cbar + cbar_axes = []; + cbarminmax_axes = []; + return; + end + + cur_fig = gcf; % save h_wait fig + figure(fig); + + if ~exist('nii_view', 'var') + nii_view = getappdata(fig, 'nii_view'); + end + + 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 + + figure(cur_fig); + + return; % create_cbar_axes + + +%---------------------------------------------------------------- +function h1 = plot_view(fig, x, y, img_ax, img_slice, clim, ... + cbarminmax, handles, useimagesc, colorindex, color_map, ... + colorlevel, highcolor, useinterp, numscan) + + h1 = []; + + if x > 1 & y > 1, + + axes(img_ax); + + nii_view = getappdata(fig, 'nii_view'); + + 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; + 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 + + else + + h1 = nii_view.handles.axial_image; + + if ~isequal(get(h1,'parent'), img_ax) + h1 = nii_view.handles.coronal_image; + end + + if ~isequal(get(h1,'parent'), img_ax) + h1 = nii_view.handles.sagittal_image; + end + + set(h1, 'cdata', double(img_slice)); + set(h1, 'xdata', 1:size(img_slice,2)); + set(h1, 'ydata', 1:size(img_slice,1)); + + end + + set(img_ax,'YDir','normal','XLimMode','manual','YLimMode','manual',... + 'ClimMode','manual','visible','off', ... + 'xtick',[],'ytick',[], 'clim', clim); + + end + + return; % plot_view + + +%---------------------------------------------------------------- +function h1 = plot_cbar(fig, cbar_axes, cbarminmax_axes, cbarminmax, ... + level, handles, useimagesc, colorindex, color_map, ... + colorlevel, highcolor, niiclass, numscan, nii_view) + + cbar_image = [1:level]'; + + % 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 + + axes(cbarminmax_axes); + + plot([0 0], cbarminmax, 'w'); + axis tight; + + set(cbarminmax_axes,'YDir','normal', ... + 'XLimMode','manual','YLimMode','manual','YColor',[0 0 0], ... + 'XColor',[0 0 0],'xtick',[],'YAxisLocation','right'); + + ylimb = get(cbarminmax_axes,'ylim'); + ytickb = get(cbarminmax_axes,'ytick'); + ytick=(ylim(2)-ylim(1))*(ytickb-ylimb(1))/(ylimb(2)-ylimb(1))+ylim(1); + + axes(cbar_axes); + + 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) + + % 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 + + 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 + + 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 + + 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); + + return % update_imgXYZ + + +%---------------------------------------------------------------- +function nii_view = convert2voxel(nii_view,slices), + + if get(nii_view.handles.coord,'value') == 1, + + % [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); + + 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); + + 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, + + % [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]); + + nii_view.axi_xhair = ... + rri_xhair(p_axi, nii_view.axi_xhair, nii_view.handles.axial_axes); + + nii_view.cor_xhair = ... + rri_xhair(p_cor, nii_view.cor_xhair, nii_view.handles.coronal_axes); + + nii_view.sag_xhair = ... + rri_xhair(p_sag, nii_view.sag_xhair, nii_view.handles.sagittal_axes); + + 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 + + img = double(img(:)); + + 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 + + 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)'); + + % 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 sagittal view + % + img_slice = squeeze(double(nii_view.disp(nii_view.slices.sag,:,:))); + + h1 = nii_view.handles.sagittal_image; + set(h1, 'cdata', double(img_slice)'); + + % 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 + + update_nii_view(nii_view); + + 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 + + if isfield(opt,'enableviewpoint') + if opt.enableviewpoint + 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 + + if isfield(opt,'enableorigin') + if opt.enableorigin + v = 'on'; + else + v = 'off'; + end + + set(handles.Torigin, 'visible', v); + set(handles.origin, 'visible', v); + end + + if isfield(opt,'enableunit') + if opt.enableunit + v = 'on'; + else + v = 'off'; + end + + set(handles.Tcoord, 'visible', v); + set(handles.coord_frame, 'visible', v); + set(handles.coord, 'visible', v); + end + + if isfield(opt,'enablecrosshair') + if opt.enablecrosshair + v = 'on'; + else + v = 'off'; + end + + set(handles.Txhair, 'visible', v); + set(handles.xhair_color, 'visible', v); + set(handles.xhair, 'visible', v); + end + + if isfield(opt,'enablehistogram') + if opt.enablehistogram + v = 'on'; + vv = 'off'; + else + v = 'off'; + vv = 'on'; + end + + set(handles.Tcoord, 'visible', vv); + set(handles.coord_frame, 'visible', vv); + set(handles.coord, 'visible', vv); + + set(handles.Thist, 'visible', v); + set(handles.hist_frame, 'visible', v); + set(handles.hist_eq, 'visible', v); + set(handles.hist_plot, 'visible', v); + end + + if isfield(opt,'enablecolormap') + if opt.enablecolormap + 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 + + if isfield(opt,'enablecontrast') + if opt.enablecontrast + 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 + + if isfield(opt,'enablebrightness') + if opt.enablebrightness + 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 + + if isfield(opt,'enabledirlabel') + if opt.enabledirlabel + 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(opt,'enableslider') + if opt.enableslider + v = 'on'; + else + v = 'off'; + end + + if isfield(handles,'sagittal_slider') & ishandle(handles.sagittal_slider) + set(handles.sagittal_slider, 'visible', v); + end + + if isfield(handles,'coronal_slider') & ishandle(handles.coronal_slider) + set(handles.coronal_slider, 'visible', v); + end + + 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 + + +%---------------------------------------------------------------- +function update_usecrosshair(fig, usecrosshair) + + if isempty(usecrosshair) + return; + end + + if usecrosshair + v=1; + else + v=2; + end + + nii_view = getappdata(fig,'nii_view'); + set(nii_view.handles.xhair,'value',v); + + 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); + +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); + + x = x + w + 0.005; + w = info_pos(3)*0.5 - 0.005; + + 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; + + 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; + + 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; + + 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); + + 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); + + 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); + + x = x + w; + w = info_pos(3)*0.2; + + 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; + + 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.7; + w = info_pos(3)*0.275; + + 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; + + 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; + + 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; + + 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; + + 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; + + 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; + + 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; + + pos = [x y w h]; + set(handles.Tcontrast,'position',pos); + + x = x + w; + w = info_pos(3)*0.18; + + 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; + + pos = [x y w h]; + set(handles.Tbrightness,'position',pos); + + x = x + w; + w = info_pos(3)*0.18; + + pos = [x y w h]; + set(handles.brightness_def,'position',pos); + + return; % update_usestretch + + +%---------------------------------------------------------------- +function update_useinterp(fig, useinterp) + + if isempty(useinterp) + return; + end + + nii_menu = getappdata(fig, 'nii_menu'); + + 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_view = getappdata(fig, 'nii_view'); + nii_view.useinterp = useinterp; + + 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 + + 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 + nii_view.handles.axial_image = image('cdata',img_slice); + end + end + + set(gca,'clim',clim); + + 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 + + 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 + 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 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 + 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); + + 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 + + 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 + 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 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 + nii_view.handles.sagittal_image = image('cdata',img_slice); + end + end + + set(gca,'clim',clim); + + 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 + + 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 + end + + set(nii_view.handles.sagittal_image,'buttondown','view_nii(''sagittal_image'');'); + end + + if ~useinterp + nii_view.bgimg = []; + end + + 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 + + nii_view = getappdata(fig,'nii_view'); + handles = nii_view.handles; + + if isfield(handles,'cbar_image') & ishandle(handles.cbar_image) +% set(handles.cbar_image,'cdatamapping',v); + end + + 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'); + + 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 + + areachange = 0; + area = nii_view.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 + + [cbar_axes cbarminmax_axes] = create_cbar_axes(fig, cbar_area); + + nii_view.area = area; + nii_view.cbar_area = cbar_area; + + else % change, set area only + areachange = 1; + nii_view.area = area; + end + + % Add colorbar + % + if ~isempty(usecolorbar) & usecolorbar & isempty(nii_view.cbar_area) + + 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 + + % create axes for colorbar + % + [cbar_axes cbarminmax_axes] = create_cbar_axes(fig, 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); + else + num_highcolor = 0; + end + + if isfield(nii_view, 'colorlevel') & ~isempty(nii_view.colorlevel) + colorlevel = nii_view.colorlevel; + else + colorlevel = 256 - num_highcolor; + end + + if isfield(nii_view, 'color_map') + color_map = nii_view.color_map; + else + color_map = []; + end + + if isfield(nii_view, 'highcolor') + highcolor = nii_view.highcolor; + else + highcolor = []; + end + + % plot colorbar + % +if 0 + if isempty(color_map) + level = colorlevel + num_highcolor; + else + level = size([color_map; highcolor], 1); + end +end + + if isempty(color_map) + level = colorlevel; + else + level = size([color_map], 1); + end + + cbar_image = [1:level]'; + + niiclass = class(nii_view.nii.img); + + 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; + + % remove colorbar + % + elseif ~isempty(usecolorbar) & ~usecolorbar & ~isempty(nii_view.cbar_area) + + colorbarchange = 1; + + area(3) = area(3) / 0.9; + + nii_view.area = area; + nii_view.cbar_area = []; + + 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'); + + else + colorbarchange = 0; + end + + 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 + + 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 + + nii_view = getappdata(fig,'nii_view'); + + if length(setviewpoint) ~= 3 + error('Viewpoint position should contain [x y z]'); + end + + set(nii_view.handles.impos,'string',num2str(setviewpoint)); + + 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'); + + return; % update_viewpoint + + +%---------------------------------------------------------------- +function update_scanid(fig, setscanid) + + if isempty(setscanid) + return; + end + + nii_view = getappdata(fig,'nii_view'); + + if setscanid < 1 + setscanid = 1; + end + + if setscanid > nii_view.numscan + setscanid = nii_view.numscan; + end + + set(nii_view.handles.contrast_def,'string',num2str(setscanid)); + set(nii_view.handles.contrast,'value',setscanid); + + opt.command = 'updateimg'; + opt.setscanid = setscanid; + + 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; + + 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); + + opt.command = 'color'; + view_nii(fig, opt); + + return; % update_colorindex + + +%---------------------------------------------------------------- +function redraw_cbar(fig, colorlevel, color_map, highcolor) + + nii_view = getappdata(fig,'nii_view'); + + if isempty(nii_view.cbar_area) + return; + end + + colorindex = nii_view.colorindex; + + if isempty(highcolor) + num_highcolor = 0; + else + num_highcolor = size(highcolor,1); + end + + if isempty(colorlevel) + colorlevel=256; + end + + if colorindex == 1 + colorlevel = size(color_map, 1); + end + +% level = colorlevel + num_highcolor; + level = colorlevel; + + cbar_image = [1:level]'; + + cbar_area = nii_view.cbar_area; + + % useimagesc follows axial image + % + if strcmpi(get(nii_view.handles.axial_image,'cdatamap'),'scaled') + useimagesc = 1; + else + useimagesc = 0; + end + + niiclass = class(nii_view.nii.img); + + delete(nii_view.handles.cbar_image); + delete(nii_view.handles.cbar_axes); + delete(nii_view.handles.cbarminmax_axes); + + [nii_view.handles.cbar_axes nii_view.handles.cbarminmax_axes] = ... + create_cbar_axes(fig, cbar_area, []); + + 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, []); + + 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); + + return; % update_buttondown + + +%---------------------------------------------------------------- +function update_cbarminmax(fig, cbarminmax) + + if isempty(cbarminmax) + return; + end + + nii_view = getappdata(fig, 'nii_view'); + + if ~isfield(nii_view.handles, 'cbarminmax_axes') + return; + end + + nii_view.cbarminmax = cbarminmax; + setappdata(fig, 'nii_view', nii_view); + + axes(nii_view.handles.cbarminmax_axes); + + plot([0 0], cbarminmax, 'w'); + axis tight; + + set(nii_view.handles.cbarminmax_axes,'YDir','normal', ... + 'XLimMode','manual','YLimMode','manual','YColor',[0 0 0], ... + 'XColor',[0 0 0],'xtick',[],'YAxisLocation','right'); + + 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); + + axes(nii_view.handles.cbar_axes); + + 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'); + + if ischar(highcolor) & (isempty(colorlevel) | nii_view.colorindex == 1) + return; + end + + 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 + + setappdata(fig, 'nii_view', nii_view); + + if isfield(nii_view,'color_map') + color_map = nii_view.color_map; + else + color_map = []; + end + + 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 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 + + 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 + + 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 + + color_map = custom_color_map; + nii_view.color_map = color_map; + end + + switch colorindex + case {2} + color_map = bipolar(num_color, cbarminmax(1), cbarminmax(2), contrast); + case {3} + color_map = gray(num_color - contrast + 1); + case {4} + color_map = jet(num_color); + case {5} + color_map = cool(num_color); + case {6} + color_map = bone(num_color); + case {7} + color_map = hot(num_color); + case {8} + color_map = copper(num_color); + case {9} + color_map = pink(num_color); + end + + nii_view.colorindex = colorindex; + + 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 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); + + color_map = [color_map; highcolor]; + + set(fig, 'colormap', color_map); + + return; % change_colormap + + +%---------------------------------------------------------------- +function move_cursor(fig) + + nii_view = getappdata(fig, 'nii_view'); + + 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'); + + 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; + + 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); + + 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'); + + sag = nii_view.slices.sag; + cor = curr(1,1); + axi = curr(1,2); + + else + + set(nii_view.handles.imvalcur,'String',' '); + set(nii_view.handles.imposcur,'String',' '); + return; + + end + + 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 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 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 + + set(nii_view.handles.imvalcur,'String',sprintf('%.6g',imgvalue)); + end + + 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, + 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); + + 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 + + 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; + + 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 + diff --git a/NIfTI_20140122/view_nii_menu.m b/NIfTI_20140122/view_nii_menu.m new file mode 100644 index 0000000..2269c93 --- /dev/null +++ b/NIfTI_20140122/view_nii_menu.m @@ -0,0 +1,480 @@ +% Imbed Zoom, Interp, and Info menu to view_nii window. +% +% Usage: view_nii_menu(fig); +% + +% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) +% +%-------------------------------------------------------------------- +function menu_hdl = view_nii_menu(fig, varargin) + + if isnumeric(fig) + menu_hdl = init(fig); + return; + end + + menu_hdl = []; + + switch fig + case 'interp' + if nargin > 1 + fig = varargin{1}; + else + fig = gcbf; + end + + nii_menu = getappdata(fig, 'nii_menu'); + 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); + else + opt.useinterp = 0; + view_nii(fig,opt); + set(nii_menu.Minterp,'Userdata',1,'Label','Interp on'); + reset_zoom(fig); + end + case 'reset_zoom' + if nargin > 1 + fig = varargin{1}; + else + fig = gcbf; + end + + reset_zoom(fig); + case 'orient' + orient; + case 'editvox' + editvox; + case 'img_info' + img_info; + case 'img_hist' + img_hist; + case 'save_disp' + save_disp; + end + + 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 + + +%---------------------------------------------------------------- +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'); + + set(nii_menu.Mzoom,'Userdata',1,'Label','Zoom on'); + set(fig,'pointer','arrow'); + zoom off; + + 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.coronal_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; + + 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 + + +%---------------------------------------------------------------- +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 + + +%---------------------------------------------------------------- +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 + + +%---------------------------------------------------------------- +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 + + old_pointer = get(gcbf,'Pointer'); + set(gcbf,'Pointer','watch'); + + nii_view = getappdata(gcbf, 'nii_view'); + nii = nii_view.nii; + + try + save_nii(nii, out_imgfile); + catch + msg = 'File can not be saved.'; + msgbox(msg, 'File write error', 'modal'); + end + + 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]); + + return; % img_hist + diff --git a/NIfTI_20140122/xform_nii.m b/NIfTI_20140122/xform_nii.m new file mode 100644 index 0000000..7252a7c --- /dev/null +++ b/NIfTI_20140122/xform_nii.m @@ -0,0 +1,521 @@ +% internal function + +% '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 +% orthogonal rotation (N*90 degree). +% +% For other affine transforms, e.g. any degree rotation, shearing +% etc. you will have to use the included 'reslice_nii.m' program +% to reslice the image volume. 'reslice_nii.m' is not called by +% any other program, and you have to run 'reslice_nii.m' explicitly +% for those NIfTI files that you want to reslice them. +% +% 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 +% orthogonally rotated, based on the affine matrix in the +% NIfTI header. +% +% However, the affine matrix in the header of a lot NIfTI files +% contain slightly non-orthogonal rotation. Therefore, optional +% input parameter 'tolerance' is used to allow some distortion +% 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. +% +% 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 +% Left to Right, Y axis from Posterior to Anterior, and Z axis +% 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) +% +% 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. +% +% 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 +% +% 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; + 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 + + % get index of orient (rotate inversely) + % + [tmp rot_orient] = sort(rot_orient); + + new_dim = old_dim; + new_dim = new_dim(rot_orient); + hdr.dime.dim([2:4]) = new_dim; + + new_pixdim = hdr.dime.pixdim([2:4]); + new_pixdim = new_pixdim(rot_orient); + hdr.dime.pixdim([2:4]) = new_pixdim; + + % 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 + end + + hdr.hist.originator([1:3]) = tmp; + hdr.hist.rot_orient = rot_orient; + hdr.hist.flip_orient = flip_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 + 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 + end + else + hdr.hist.rot_orient = []; + hdr.hist.flip_orient = []; + end + + 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)]; + + 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 + end + + 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 + 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 + + else + R = R * diag([i j k]); + end % 1st det(R) + + 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); + + % 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 + + % 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; + + % 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); + + return; % change_hdr + + +%----------------------------------------------------------------------- +function orient = get_orient(R) + + orient = []; + + for i = 1:3 + switch find(R(i,:)) * sign(sum(R(i,:))) + case 1 + orient = [orient 1]; % Left to Right + case 2 + orient = [orient 2]; % Posterior to Anterior + case 3 + orient = [orient 3]; % Inferior to Superior + case -1 + orient = [orient 4]; % Right to Left + case -2 + orient = [orient 5]; % Anterior to Posterior + case -3 + orient = [orient 6]; % Superior to Inferior + end + end + + 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 + diff --git a/README.md b/README.md index 9e3c132..5524a10 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,43 @@ # MRItoolkit A series of MATLAB program which can accelerate the processing of MR images. + +Attention! To use those toolkit, an MATLAB toolbox name NIfTI should be set as a path of MATLAB software.(NIfTI_20140122 folder is the toolbox for you) + +1.Auto_T1_T2_DWI_selection.m +Classify the raw MRI data into T1, T2 and DWI by the information of raw data. + +2.autoDWIsprlitbytime.m +Useless. + +3.DiComDifferenceExam.m +Exam the difference between different DiCom files. + +4.DTI_bvec_DiffutionToolkit_allfilesread.m +Convert all the b vector files into a form which can be used by diffusion toolkit software. + +5.rotate_for_trackvis.m +Extract special ROI template information for TrackVis software. + +6.SeparationByScanningDate.m +Separate the Dicom raw data into different groups by the scanning date. + +7.standard_space_sorting.m +Sorting the output files of PANDA software, which scanning special fields such as 'FA', 'LDHs', 'LDHk'and save them to a mat, then export them into an excel. + +8.Mean_FA.m +For the calculation of Mean FA value. + +9.CIN_network_extraction_toolkit.m +Extraction CIN(lable 31:36 in AAL 90 atlas set)to show the construction + +10.CIN_network_extraction_toolkit_with_split.m +Extract the FA brain networks which are not ioslated from other nodes. + +11.img2txt.m, img2txt_new_imgshow, img2txt_new_txtchech.m +All of them are used for the construction of VR brain which makes the preparation for the Unity softwares. + +12.Network_sorting.m +Sorting the output files of Networks of PANDA software. + +13.standard_space_sorting.m +Sorting the standard space output files of PANDA software. diff --git a/Readme.txt b/Readme.txt deleted file mode 100644 index 74435aa..0000000 --- a/Readme.txt +++ /dev/null @@ -1,29 +0,0 @@ -1.Auto_T1_T2_DWI_selection.m -用于自动化的从原始数据中提取出T1,T2,DWI数据并建立文件夹保存。 - -2.autoDWIsprlitbytime.m -没有用,一开始是想为PANDA做的 - -3.DiComDifferenceExam.m -是那时候和毛帅为了验证两个Dicom文件改变后的差异分析,平常用不到 - -4.DTI_bvec_DiffutionToolkit_allfilesread.m -用于一次性将目录下所有bvector文件转换为Diffusion Toolkit可用的格式。 - -5.rotate_for_trackvis.m -用于提取我们感兴趣的ROI模板信息,来加载到tracvis软件中进行特定的纤维提取。 - -6.SeparationByScanningDate.m -这个还能有点用处,如果我们得到的Dicom文件时不同日期获取的图像,但是却糅杂在了一起,我们便可以用这个小程序来进行按照日期的划分工作。 - -7.standard_space_sorting.m -用于对PANDA输出结果中的标准空间下的结果进行筛选,选出我们需要的包含特定字段(比如'FA','LDHs','LDHk')的文件,存储到一张mat表下,并输出为xls格式。 - -8.Mean_FA.m -用于计算平均的FA值 - -9.CIN_network_extraction_toolkit.m: -Extraction CIN(lable 31:36 in AAL 90 atlas set)to show the construction - -10.CIN_network_extraction_toolkit_with_split.m -分割出我们需要的FA扣带回相关网络中那些非孤立点的连接关系矩阵