ind2patch
Below is a demonstration of the features of the ind2patch function
Contents
- Plotting a selection of voxels and slices
- Introduction to using ind2patch for voxel plotting
- Introduction to using ind2patch for slice plotting
- Comparison to standard MATLAB imagesc function and the patch type MATLAB functions slice and pcolor
- Creating and plotting combined voxel and slice patch data
- Shrinking patch data through combination with scalePatch function
- Example medical image data and coordinate manipulation due to voxel size
- Combining colormap and RGB driven patch colours
clear all; close all; clc;
Plotting a selection of voxels and slices
Plot settings
fig_color='w'; fig_colordef='white'; cMap=jet(250); faceAlpha1=1; faceAlpha2=0.65; edgeColor1='none'; edgeColor2='none';
Simulating an image
M=rand(2,3,4);
Introduction to using ind2patch for voxel plotting
The voxel for which patch data is to be specified can be defined by supplying linear indices or a logic array.
% Example supplying linear indices, here all voxels
indPatch=1:numel(M);
Creating patch data for voxel display
[F,V,C]=ind2patch(indPatch,M,'v'); figuremax(fig_color,fig_colordef); title('patch type: v'); xlabel('J - columns');ylabel('I - rows'); zlabel('K - slices'); hold on; patch('Faces',F,'Vertices',V,'FaceColor','flat','CData',C,'EdgeColor','k','FaceAlpha',0.5); axis equal; view(3); axis tight; axis vis3d; grid off; colormap(cMap); caxis([min(M(:)) max(M(:))]); drawnow;

Study the size of the face and vertex arrays to confirm that the patch type 'v' creates vertices and faces for each voxel. Shared vertices and faces are not removed. This option requires the most memory.
disp(num2str(size(F))); disp(num2str(size(V)));
144 4 192 3
Creating patch data for voxel display with shared vertices and faces removed
[F,V,C]=ind2patch(indPatch,M,'vu'); figuremax(fig_color,fig_colordef); title('patch type: vu'); xlabel('J - columns');ylabel('I - rows'); zlabel('K - slices'); hold on; patch('Faces',F,'Vertices',V,'FaceColor','flat','CData',C,'EdgeColor','k','FaceAlpha',0.5); axis equal; view(3); axis tight; axis vis3d; grid off; colormap(cMap); caxis([min(M(:)) max(M(:))]); drawnow;

Study the size of the face and vertex arrays to confirm that the patch type 'vu' ensures that shared faces and vertices are not shared. Each vertex and face is therefore unique. This saves memory with respect to using the 'v' patch type for voxel display. The figures look identical except that shared faces may appear less dark when transparency is on since now one one face is used. Color information is shared too (averaged).
disp(num2str(size(F))); disp(num2str(size(V)));
98 4 60 3
Creating patch data for voxel display with only non-shared faces and vertices
[F,V,C]=ind2patch(indPatch,M,'vb'); figuremax(fig_color,fig_colordef); title('patch type: vb'); xlabel('J - columns');ylabel('I - rows'); zlabel('K - slices'); hold on; patch('Faces',F,'Vertices',V,'FaceColor','flat','CData',C,'EdgeColor','k','FaceAlpha',0.5); axis equal; view(3); axis tight; axis vis3d; grid off; colormap(cMap); caxis([min(M(:)) max(M(:))]); drawnow;

Study the size of the face and vertex arrays to confirm that the patch type 'vb' helps to plot only non-shared vertices and faces. In the case of an enclosed shape filled with voxels thus only the boundary faces are displayed. This path type appears no different than the other is transparency is not on however it is much lighter on memory than the above path types.
disp(num2str(size(F))); disp(num2str(size(V)));
52 4 54 3
Introduction to using ind2patch for slice plotting
figuremax(fig_color,fig_colordef); title('patch type: si, sj, sk'); xlabel('J - columns');ylabel('I - rows'); zlabel('K - slices'); hold on; %Setting up indices for I direction slices S=round(size(M,1)./2); %Selection of middle slice L_plot=false(size(M)); L_plot(S,:,:)=1; IND=find(L_plot); [F,V,C]=ind2patch(IND,M,'si'); %Creating patch data for y mid-voxel slices hs=patch('Faces',F,'Vertices',V,'EdgeColor','k', 'CData',C,'FaceColor','flat','FaceAlpha',1); %Setting up indices for J direction slices S=round(size(M,2)./2); %Selection of middle slice L_plot=false(size(M)); L_plot(:,S,:)=1; IND=find(L_plot); [F,V,C]=ind2patch(IND,M,'sj'); %Creating patch data for x mid-voxel slices hs=patch('Faces',F,'Vertices',V,'EdgeColor','k', 'CData',C,'FaceColor','flat','FaceAlpha',1); %Setting up indices for Z direction slices S=round(size(M,3)./2); %Selection of middle slice L_plot=false(size(M)); L_plot(:,:,S)=1; IND=find(L_plot); [F,V,C]=ind2patch(IND,M,'sk'); %Creating patch data for z mid-voxel slices hs=patch('Faces',F,'Vertices',V,'EdgeColor','k', 'CData',C,'FaceColor','flat','FaceAlpha',1); axis equal; view(3); axis tight; axis vis3d; grid on; colormap(cMap); caxis([min(M(:)) max(M(:))]); drawnow;

The path type s*u are simular to s* but use shared vertices
Comparison to standard MATLAB imagesc function and the patch type MATLAB functions slice and pcolor
figuremax(fig_color,fig_colordef); subplot(2,2,1); title('MATLAB imagesc function'); xlabel('J - columns');ylabel('I - rows'); zlabel('K - slices'); hold on; imagesc(M(:,:,S)); axis equal; view(2); axis tight; axis vis3d; axis xy; grid on; colormap(cMap); caxis([min(M(:)) max(M(:))]); subplot(2,2,2); title('MATLAB slice function'); xlabel('J - columns');ylabel('I - rows'); zlabel('K - slices'); hold on; slice(M,[],[],S); axis equal; view(2); axis tight; axis vis3d; grid on; colormap(cMap); caxis([min(M(:)) max(M(:))]); subplot(2,2,4); title('MATLAB pcolor function'); xlabel('J - columns');ylabel('I - rows'); zlabel('K - slices'); hold on; pcolor(M(:,:,S)); axis equal; view(2); axis tight; axis vis3d; grid on; colormap(cMap); caxis([min(M(:)) max(M(:))]); subplot(2,2,3); title('ind2patch function'); xlabel('J - columns');ylabel('I - rows'); zlabel('K - slices'); hold on; hs=patch('Faces',F,'Vertices',V,'EdgeColor','none', 'CData',C,'FaceColor','flat','FaceAlpha',1); axis equal; view(2); axis tight; axis vis3d; grid on; colormap(cMap); caxis([min(M(:)) max(M(:))]); drawnow;

Note that the image size is wrong for the slice and pcolor commands. This is because intensities appear to be defined on voxels vertices (and are reinterpolated onto faces) for these function instead of voxels centres as should be the case for image data.
Creating and plotting combined voxel and slice patch data
Simulating 3D image
[X,Y,Z]=meshgrid(linspace(-4.77,4.77,25)); phi=(1+sqrt(5))/2; M=1/6*(2 - (cos(X + phi*Y) + cos(X - phi*Y) + cos(Y + phi*Z) + cos(Y - phi*Z) + cos(Z - phi*X) + cos(Z + phi*X)));
Creating and plotting patch data. Last example illustrates the use of a specific logic description (i.e. a mask) for the voxels of interest
figuremax(fig_color,fig_colordef); title('patch type: si, sj, sk, vb'); xlabel('J - columns');ylabel('I - rows'); zlabel('K - slices'); hold on; % Setting up indices for I direction slices S=round(size(M,1)./2); %Selection of middle slice L_plot=false(size(M)); L_plot(S,:,:)=1; IND=find(L_plot); [F,V,C]=ind2patch(IND,M,'si'); %Creating patch data for y mid-voxel slices hs=patch('Faces',F,'Vertices',V,'EdgeColor','none', 'CData',C,'FaceColor','flat','FaceAlpha',0.75); % Setting up indices for J direction slices S=round(size(M,2)./2); %Selection of middle slice L_plot=false(size(M)); L_plot(:,S,:)=1; IND=find(L_plot); [F,V,C]=ind2patch(IND,M,'sj'); %Creating patch data for x mid-voxel slices hs=patch('Faces',F,'Vertices',V,'EdgeColor','none', 'CData',C,'FaceColor','flat','FaceAlpha',0.75); % Setting up indices for K direction slices S=round(size(M,3)./2); %Selection of middle slice L_plot=false(size(M)); L_plot(:,:,S)=1; IND=find(L_plot); [F,V,C]=ind2patch(IND,M,'sk'); %Creating patch data for z mid-voxel slices hs=patch('Faces',F,'Vertices',V,'EdgeColor','none', 'CData',C,'FaceColor','flat','FaceAlpha',0.75); % Setting up indices for voxels to plot L_mask=M>-0.2 & M<0; [F,V,C]=ind2patch(L_mask,M,'vb'); %Creating patch data for selection of high voxels hs=patch('Faces',F,'Vertices',V,'EdgeColor','k', 'CData',C,'FaceColor','flat','FaceAlpha',1); colormap(cMap); colorbar; caxis([min(M(:)) max(M(:))]); axis equal; view(3); axis tight; colormap jet; grid on;

Shrinking patch data through combination with scalePatch function
% Using the function |scalePatch| patch data can be shrunk to aid % visualisation (can be a tool to avoid memory costly transparency for % instance). [Ev,Vv,Cv]=ind2patch(L_mask,M,'h'); %This creates a hexahedral element for each voxel [Evs,Vvs,Cvs]=scalePatch(Ev,Vv,Cv,0.5); %Apply voxel element scaling [Fvs,Cvs]=element2patch(Evs,Cvs); %Convert to quad faces for plotting
Plotting the voxels
h1=figuremax(fig_color,fig_colordef); title('Scaled (shrunk) patch data'); xlabel('J - columns');ylabel('I - rows'); zlabel('K - slices'); hold on; hp1= patch('Faces',Fvs,'Vertices',Vvs,'FaceColor','flat','CData',Cvs,'EdgeColor','k','FaceAlpha',faceAlpha1); axis equal; view(3); axis tight; axis vis3d; grid on; colormap(cMap); colorbar; caxis([min(M(:)) max(M(:))]); drawnow;

Example medical image data and coordinate manipulation due to voxel size
% Get a 3D image load mri; M=squeeze(D); %example image data set v=2./[1,1,.4]; %example voxel size
The voxels to display can be specified as a list (vector) of voxels numbers (linear indices) or using a mask (logic array).
%Defining row, column and slice indicices for slice patching sliceIndexI=round(size(M,1)/2); %(close to) middle row sliceIndexJ=round(size(M,2)/2); %(close to) middle column sliceIndexK=round(size(M,3)/2); %(close to) middle slice %Defining "masks" i.e. logic arrays with ones for voxels of interest logicSliceI=false(size(M)); logicSliceI(sliceIndexI,:,:)=1; logicSliceI=logicSliceI & M>0; logicSliceJ=false(size(M)); logicSliceJ(:,sliceIndexJ,:)=1; logicSliceJ=logicSliceJ & M>0; logicSliceK=false(size(M)); logicSliceK(:,:,sliceIndexK)=1; logicSliceK=logicSliceK & M>0; %Defining voxel indices for voxels of interest T_low=min(M(:))+((max(M(:))-min(M(:)))/10); %Threshold example logicVoxels=(M>T_low); logicVoxels(:,1:sliceIndexJ,:)=0;
Creating patch data The patch data consists of a matrix array defining the faces, a matrix array defining the vertices and a vector for the colour data. The vertices are based on the image coordinates however they are formatted as: [X(:) Y(:) Z(:)]. X relates to columns, Y to rows and Z to slices. Use a function like im2cart , or im2mrcart to convert image to cartesian coordinates.
[Fv,Vv,Cv]=ind2patch(logicVoxels,M,'vb'); [Fx,Vx,Cx]=ind2patch(logicSliceJ,M,'sj'); [Fy,Vy,Cy]=ind2patch(logicSliceI,M,'si'); [Fz,Vz,Cz]=ind2patch(logicSliceK,M,'sk'); % Convert image coordinates to cartesian coordinates [Vv(:,1),Vv(:,2),Vv(:,3)]=im2cart(Vv(:,2),Vv(:,1),Vv(:,3),v); [Vx(:,1),Vx(:,2),Vx(:,3)]=im2cart(Vx(:,2),Vx(:,1),Vx(:,3),v); [Vy(:,1),Vy(:,2),Vy(:,3)]=im2cart(Vy(:,2),Vy(:,1),Vy(:,3),v); [Vz(:,1),Vz(:,2),Vz(:,3)]=im2cart(Vz(:,2),Vz(:,1),Vz(:,3),v); h1=figuremax(fig_color,fig_colordef); title('MRI visualisation, slices and voxels in cartesian coordinates with aid of voxel size'); xlabel('X (mm)');ylabel('Y (mm)'); zlabel('Z (mm)'); hold on; hp1= patch('Faces',Fv,'Vertices',Vv,'FaceColor','flat','CData',Cv,'EdgeColor',edgeColor1,'FaceAlpha',faceAlpha1); hp2= patch('Faces',Fx,'Vertices',Vx,'FaceColor','flat','CData',Cx,'EdgeColor',edgeColor2,'FaceAlpha',faceAlpha1); hp3= patch('Faces',Fy,'Vertices',Vy,'FaceColor','flat','CData',Cy,'EdgeColor',edgeColor2,'FaceAlpha',faceAlpha1); hp4= patch('Faces',Fz,'Vertices',Vz,'FaceColor','flat','CData',Cz,'EdgeColor',edgeColor2,'FaceAlpha',faceAlpha1); axis equal; view(3); axis tight; axis vis3d; grid on; colormap(cMap); colorbar; drawnow;

Combining colormap and RGB driven patch colours
N.B. The figure renderer might have to be set to OPENGL. This is the default renderer for the figuremax function
Convert voxels colouring to RGB type, here a simple conversion to a gray scale description is used
Cv=(Cv*ones(1,3))./max(Cv(:));
Plotting the voxels
h1=figuremax(fig_color,fig_colordef); title('MRI visualisation, slices and voxels, colormap and RGB driven respectively'); xlabel('X (mm)');ylabel('Y (mm)'); zlabel('Z (mm)'); hold on; hp1= patch('Faces',Fv,'Vertices',Vv,'FaceColor','flat','FaceVertexCData',Cv,'EdgeColor',edgeColor1,'FaceAlpha',faceAlpha2); hp2= patch('Faces',Fx,'Vertices',Vx,'FaceColor','flat','CData',Cx,'EdgeColor',edgeColor2,'FaceAlpha',faceAlpha2); hp3= patch('Faces',Fy,'Vertices',Vy,'FaceColor','flat','CData',Cy,'EdgeColor',edgeColor2,'FaceAlpha',faceAlpha2); hp4= patch('Faces',Fz,'Vertices',Vz,'FaceColor','flat','CData',Cz,'EdgeColor',edgeColor2,'FaceAlpha',faceAlpha2); axis equal; view(3); axis tight; axis vis3d; grid on; colormap(cMap); colorbar; drawnow;

GIBBON
Kevin M. Moerman (kevinmoerman@hotmail.com)