Working with voxel fomat

Often imaging data is provided in voxel format. However, as ValoMC uses tetrahedrons as the basis elements, the data are not directly compatible. This example demonstrates how to move between the two formats.

Contents

Creating a rectangular 3d mesh

To create a mesh that can be easily mapped to a voxel grid the function createGridMesh can be used

clear all;

x_arr = -2:1:2;
y_arr = -2:1:2;
z_arr = -2:1:2;

vmcmesh = createGridMesh(x_arr, y_arr, z_arr); % function provided by ValoMC

nvoxels_total = length(x_arr)*length(y_arr)*length(z_arr);
voxels_in_a_yx_slice = length(y_arr)*length(x_arr);

Visualization of the mesh

The structure of the mesh is similar as in the 2d version (see pixeltest.m). Each voxel consists of 6 tetrahedrons. Vectors x_arr, y_arr and z_arr contain the center of each voxel. The elements 1 to nvoxels_total contain the first tetrahedron in a voxel, nvoxels_total to 2*nvoxels_total the second and so on. The elements are ordered in the same fashion as the coordinates in meshgrid i.e. Y ascends first, then X and finally Z. Illustration of the element indices is given in the figure below.

tetramesh(vmcmesh.H(1:voxels_in_a_yx_slice,:),vmcmesh.r, 'FaceAlpha', ...
          0.1);
hold on;
xlabel('x');
ylabel('y');
zlabel('z');

% draw the element numbers
for i=1:voxels_in_a_yx_slice
   element_center = (vmcmesh.r(vmcmesh.H(i,1),:) + vmcmesh.r(vmcmesh.H(i,2),:) ...
                     + vmcmesh.r(vmcmesh.H(i,3),:) + vmcmesh.r(vmcmesh.H(i,4),:)) * 0.25;
   text(element_center(1), element_center(2), element_center(3), num2str(i));
end

view(-110,50);
snapnow;
hold off;

% Create a finer mesh

x_arr = -2:0.1:2;
y_arr = -2:0.1:2;
z_arr = -2:0.1:2;

vmcmesh = createGridMesh(x_arr, y_arr, z_arr); % function provided by ValoMC
vmcmedium = createMedium(vmcmesh);

Create an anisotropic parameter distribution

[X,Y,Z] = meshgrid(x_arr,y_arr,z_arr); % Matlab function
F = 1.3+cos(X*3).*cos(Y*3).*cos(Z*3)*0.2+0.2;
slice(X, Y, Z, F, 0, 0, 0);
xlabel('x [mm]');
ylabel('y [mm]');
zlabel('z [mm]');
c=colorbar;
c.Label.String = 'Refractive index';
view(125,25);
snapnow;

Accessing elements using one dimensional indexing

Note that since there are six times as many tetrahedrons as there are grid cells, vmcmedium.absorption_coefficient is six times bigger than F A complete assignment can be achieved by repeating the array F six times

vmcmedium.scattering_coefficient = 1.0;
vmcmedium.absorption_coefficient = repmat(F(:),6,1); % repeat six times

vmcmedium.scattering_anisotropy = 0.9;
vmcmedium.refractive_index = 1;

vmcboundary = createBoundary(vmcmesh, vmcmedium);   % create a boundary for the mesh

% Create a light source
lightsource = findBoundaries(vmcmesh, 'direction', [0 0 0], [0 0 10], 1);
vmcboundary.lightsource(lightsource) = {'cosinic'};


solution = ValoMC(vmcmesh, vmcmedium, vmcboundary);
                 ValoMC-3D
--------------------------------------------
  Version:  v1.0b-118-g853f111
  Revision: 131
  OpenMP enabled                     
  Using 16 threads
--------------------------------------------
Initializing MC3D...
Computing... 
...done

Done

Visualize the solution

TR = triangulation(double(vmcmesh.H),vmcmesh.r); % create a matlab
                                           % triangulation object
                                           % from the mesh

locations = [X(:) Y(:) Z(:)];              % form a 2D matrix from all
                                           % the grid points

indices = pointLocation(TR,locations);     % query the indices of the
                                           % tetrahedrons at grid
                                           % points

indices(isnan(indices)) = 1;               % set the grid points that
                                           % do not belong to the mesh
                                           % to point at the first
                                           % element

% get the values on a grid
grid_fluence = reshape(solution.element_fluence(indices),size(X));


slice(X, Y, Z, grid_fluence, 0, 0, 0);
xlabel('x [mm]');
ylabel('y [mm]');
zlabel('z [mm]');

view(125,25);
snapnow;

Accessing elements using three dimensional indexing

Optionally, the medium can be defined using three-dimensional indexing. If three dimensional indexing is used, ValoMC will assume that createGridMesh has been used to create the mesh. In addition to the solution.element_fluence, ValoMC will return solution.grid_fluence, which represents the fluence in each voxel. It is calculated as a sum of the tetrahedrons in a grid cell.

clear vmcmedium;
clear vmcboundary;

vmcmedium.scattering_coefficient = 1.0;
vmcmedium.absorption_coefficient = F;  %refractive index is now a three dimensional array
vmcmedium.scattering_anisotropy = 0.9;
vmcmedium.refractive_index = 1;

vmcboundary = createBoundary(vmcmesh, vmcmedium);

lightsource = findBoundaries(vmcmesh, 'direction', [0 0 0], [0 0 10], 1);
vmcboundary.lightsource(lightsource) = {'cosinic'};


solution = ValoMC(vmcmesh, vmcmedium, vmcboundary);
                 ValoMC-3D
--------------------------------------------
  Version:  v1.0b-118-g853f111
  Revision: 131
  OpenMP enabled                     
  Using 16 threads
--------------------------------------------
Initializing MC3D...
Computing... 
...done

Done

Visualize the solution as a voxel map

Since 3D array was used to define the scattering coefficient, solution returned contains the field grid_fluence

slice(X, Y, Z, solution.grid_fluence, 0, 0, 0);
xlabel('x [mm]');
ylabel('y [mm]');
zlabel('z [mm]');
view(125,25);
hold

snapnow;
Current plot held