Code covered by the BSD License  

Highlights from
3D Slicer

image thumbnail
from 3D Slicer by David Legland
Slicer for exploring 3D images (grayscale, color or vectorial) through planar or 3D slices.

slice3d.m
function hs = slice3d(img, dim, index, varargin)
%SLICE3D Show a moving 3D slice of an image
%
%   slice3d(IMG, DIR)
%   slice3d(IMG, DIR, INDEX)
%   IMG is a 3D (grayscale) or 4D (color) image, with size Ny-by-Nx-by-Nz
%   or Ny-by-Nx-by-3-by-Nz. 
%   DIM is the direction of slicing, that can be:
%   1, corresponding to the 'x' direction (array direction n 2)
%   2, corresponding to the 'y' direction (array direction n 1)
%   3, corresponding to the 'z' direction (array direction n 3)
%   INDEX is the index of the slice in the corresponding direction. Default
%   is the middle of the image in the given direction.
%
%   slice3d(IMG, DIR, INDEX, SCALE)
%   give the voxel resolution for each one of the x,y,z directions.
%
%   slice3d(..., 'DisplayRange', RANGE)
%   Specifies the grayscale range of the data. RANGE should be a 1-by-2 row
%   vector containing min and max values to display. Display will be
%   adjusted such that min value correspond to black (value 0), and max
%   value correspond to white (value 255).
%
%   slice3d(..., 'ColorMap', MAP)
%   Specifies the color map to display the data with. MAP should be a
%   255-by-3 array, see the colromap function for examples.
%
%   See also
%   orthoSlices, orthoSlices3d, colormap
%
%   References
%   Largely inspired by file 'slice3i' from Anders Brun, see FEx:
%   http://www.mathworks.fr/matlabcentral/fileexchange/25923
%
% ------
% Author: David Legland
% e-mail: david.legland@grignon.inra.fr
% Created: 2011-03-02,    using Matlab 7.9.0.529 (R2009b)
% Copyright 2011 INRA - Cepia Software Platform.


%% Input arguments extraction

% get stack size (in x, y, z order)
siz = stackSize(img);

% use a default position if not specified
if nargin < 3
    index = ceil(siz/2);
end

% check axis direction
dim = parseAxisIndex(dim);

% extract spatial calibration
if ~isempty(varargin) && isnumeric(varargin{1})
    var = varargin{1};
    if numel(var) == 3
        % resolution is given for each x, y, and z directions
        dcm = diag([var 1]);
        
    elseif all(size(var) >= [3 3])
        % resolution is given as a transformation matrix
        dcm = var;
        dcm(4, 4) = 1;
        
    else
        error('Unable to parse spatial calibration');
    end
    varargin(1) = [];
    
else
    % no spatial calibration: use identity matrix
    dcm = eye(4);
end

% default display calibration
displayRange = [0 255];
lut = [];

% extract display calibration
while length(varargin) > 1
    param = varargin{1};
    switch lower(param)
        case 'displayrange'
            displayRange = varargin{2};
        case {'lut', 'colormap'}
            lut = varargin{2};
        otherwise
            error(['Unknown parameter: ' param]);
    end
    varargin(1:2) = [];
end


%% Extract and normalise slice

% extract the slice
slice = stackSlice(img, dim, index);

slice = computeSliceRGB(slice, displayRange, lut);


%% Extract slice coordinates

% 
switch dim
    case 1
        % X Slice
        
        % compute coords of u and v
        vy = ((0:siz(2)) + .5);
        vz = ((0:siz(3)) + .5);
        [ydata zdata] = meshgrid(vy, vz);
        
        % coord of slice supporting plane
        lx = 1:siz(1);
        xdata = ones(size(ydata)) * lx(index);
        
   case 2
        % Y Slice
       
        % compute coords of u and v
        vx = ((0:siz(1)) + .5);
        vz = ((0:siz(3)) + .5);
        [zdata xdata] = meshgrid(vz, vx);

        % coord of slice supporting plane
        ly = 1:siz(2);
        ydata = ones(size(xdata)) * ly(index);

    case 3
        % Z Slice

        % compute coords of u and v
        vx = ((0:siz(1)) + .5);
        vy = ((0:siz(2)) + .5);
        [xdata ydata] = meshgrid(vx, vy);
        
        % coord of slice supporting plane
        lz = 1:siz(3);
        zdata = ones(size(xdata)) * lz(index);
        
    otherwise
        error('Unknown stack direction');
end

% transform coordinates from image reference to spatial reference
hdata = ones(1, numel(xdata));
trans = dcm(1:3, :) * [xdata(:)'; ydata(:)'; zdata(:)'; hdata];
xdata(:) = trans(1,:); 
ydata(:) = trans(2,:); 
zdata(:) = trans(3,:); 


%% Display slice in 3D

% global parameters for surface display
params = [{'facecolor', 'texturemap', 'edgecolor', 'none'}, varargin];

% display voxel values in appropriate reference space
hs = surface(xdata, ydata, zdata, slice, params{:});


% set up slice data
data.handle = hs;
data.img    = img;
data.dim    = dim;
data.index  = index;
data.dcm    = dcm;
data.displayRange = displayRange;
data.lut    = lut;
set(hs, 'UserData', data);

% set up mouse listener
set(hs, 'ButtonDownFcn', @startDragging);



function startDragging(src, event) %#ok<INUSD>
%STARTDRAGGING  One-line description here, please.
%
%   output = startDragging(input)
%
%   Example
%   startDragging
%
%   See also
%

data = get(src, 'UserData');

% store data for creating ray
data.startRay   = get(gca, 'CurrentPoint');
data.startIndex = data.index;

% update data of slice object
set(src, 'UserData', data);

% store reference to slice object in figure object
hFig = gcbf();
set(hFig, 'UserData', src);

% set up listeners for figure object
set(hFig, 'WindowButtonMotionFcn', @dragSlice);
set(hFig, 'WindowButtonUpFcn', @stopDragging);


function stopDragging(src, event) %#ok<INUSD>
%STOPDRAGGING  One-line description here, please.
%
%   output = stopDragging(input)
%
%   Example
%   stopDragging
%
%   See also
%


% remove figure listeners
hFig = src;
set(hFig, 'WindowButtonUpFcn', '');
set(hFig, 'WindowButtonMotionFcn', '');

% get slice reference
hs   = get(src, 'UserData');
data = get(hs, 'UserData');

% reset slice data
data.startray = [];
set(hs, 'UserData', data);

% reset figure data
set(hFig, 'UserData', []);

% update display
drawnow;


function dragSlice(src, event) %#ok<INUSD>
%DRAGSLICE  One-line description here, please.
%
%   output = dragSlice(input)
%
%   Example
%   dragSlice
%
%   See also
%


%% Extract data

% Extract slice data
hs      = get(src, 'UserData');
data    = get(hs, 'UserData');

% basic checkup
if ~isfield(data, 'startRay')
    return;
end
if isempty(data.startRay)
    return;
end

if ~isfield(data, 'dcm')
    data.dcm = eye(4);
end


%% Compute new slice index

% dimension in xyz
dim = data.dim;

% get the ray used for computing slice position
sliceNormal = data.dcm(1:3, [4 dim])';

% position of initial ray
pos0 = posProjRayOnRay(data.startRay, sliceNormal);

% position of current ray
currentRay = get(gca, 'CurrentPoint');
pos = posProjRayOnRay(currentRay, sliceNormal);

% compute difference in positions
slicediff = pos - pos0;

index = data.startIndex + round(slicediff);
index = min(max(1, index), stackSize(data.img, data.dim));
data.index = index;

% Store gui object
set(hs, 'UserData', data);


%% Update content of the surface object with current slice

% extract slice corresponding to current index
slice = stackSlice(data.img, data.dim, data.index);

% convert to renderable RGB
slice = computeSliceRGB(slice, data.displayRange, data.lut);

% setup display data
set(hs, 'CData', slice);


%% Update position of the slice along its direction

imgSize = size(data.img);

% the mesh used to render image has one element more, to enclose all pixels
meshSize = [size(slice, 1) size(slice, 2)] + 1;

isOrtho = sum(abs(data.dcm([2 3 5 7 9 10]))) < 1e-12;

if isOrtho
    % in the case of scaling + translation, use simple processing (faster)
    switch data.dim
        case 1
            xpos = data.index * data.dcm(1,1) + data.dcm(1,4);
            xdata = ones(meshSize) * xpos;
            set(hs, 'xdata', xdata);
            
        case 2
            ypos = data.index * data.dcm(2,2) + data.dcm(2,4);
            ydata = ones(meshSize) * ypos;
            set(hs, 'ydata', ydata);
            
        case 3
            zpos = data.index * data.dcm(3,3) + data.dcm(3,4);
            zdata = ones(meshSize) * zpos;
            set(hs, 'zdata', zdata);
            
        otherwise
            error('Unknown stack direction');
    end
    
else
    % for general matrices, computes transformed coordinates (seems to be
    % slower)
    switch data.dim
        case 1
            % compute coords of u and v
            vy = ((0:imgSize(1)) + .5);
            vz = ((0:imgSize(3)) + .5);
            [ydata zdata] = meshgrid(vy, vz);

            lx = 1:imgSize(2);
            xdata = ones(meshSize) * lx(index);

        case 2
            % compute coords of u and v
            vx = ((0:imgSize(2)) + .5);
            vz = ((0:imgSize(3)) + .5);
            [zdata xdata] = meshgrid(vz, vx);

            ly = 1:imgSize(1);
            ydata = ones(meshSize) * ly(index);

        case 3
            % compute coords of u and v
            vx = ((0:imgSize(2)) + .5);
            vy = ((0:imgSize(1)) + .5);
            [xdata ydata] = meshgrid(vx, vy);

            lz = 1:imgSize(3);
            zdata = ones(meshSize) * lz(index);

        otherwise
            error('Unknown stack direction');
    end

    % transform coordinates from image reference to spatial reference
    hdata = ones(1, numel(xdata));
    trans = data.dcm(1:3,:) * [xdata(:)'; ydata(:)'; zdata(:)'; hdata];
    xdata(:) = trans(1,:); 
    ydata(:) = trans(2,:); 
    zdata(:) = trans(3,:); 
    set(hs, 'xdata', xdata, 'ydata', ydata, 'zdata', zdata);
end

% update display
drawnow;


function pos = posProjRayOnRay(ray1, ray2)
% ray1 and ray2 given as 2-by-3 arrays

u = ray1(2,:) - ray1(1,:);
v = ray2(2,:) - ray2(1,:);
w = ray1(1,:) - ray2(1,:);

a = dot(u, u, 2);
b = dot(u, v, 2);
c = dot(v, v, 2);
d = dot(u, w, 2);
e = dot(v, w, 2);

pos = (a*e - b*d) / (a*c - b^2);

Contact us