Extensions/MatlabBridge: Send transform (4x4 matrix) from Matlab to 3D Slicer

Hi,
I’m beginning to work now with Matlab. I’ve looked over descriptions and questions but some questions are not clear for me yet. So I was wondering if somebody could help me out:
I wanted to ask if it is possible to visualize a filtered volume (based on matrices 181x299x305 double - created from image slices) from a Matlab file in 3D Slicer via the MatlabBridge Module Generator? Or whether I need to take the OpenIGTLink where I would need to create libraries (…) first (based on Mex files? Is it possible to see the volume then from the 3 different angles? Can this volume be adjusted with certain modules too?
I’m very thankful for any kind of suggestions!

Yes. MatlabBridge Module Generator creates a skeleton that you customize to call the Matlab function you need.

Note that when you use Slicer’s MatlabBridge, Slicer drives the workflow: you load input volumes and other data into Slicer, choose processing inputs and outputs, and initiate processing in Slicer, and then when processing is completed, results automatically transferred to and displayed in Slicer.

Slicer uses OpenIGTLink under the hood to communicate with the Matlab process, but you will not need to worry about these details. There is no need to build any mex files (avoiding mex files was one of the important design requirements of MatlabBridge, as it is a nightmare to maintain them) - MatlabBridge includes an OpenIGTLink server implemented in native Matlab script. But again, it is an internal detail that should not matter for you.

Yes, the full arsenal of Slicer’s visualization tools are available for you. You can view any number of slices, in any orientation, volume renderings, surface renderings, fusing multiple volumes, etc.

Yes. You can implement complete processing workflows as a mixture of Slicer core modules and your Matlab modules.

I can give more specific answers if you describe the clinical problem that you would like to solve and how you plan to approach it.

Thank you very much for this detailed reply!
I´m trying to display in the 3d slicer a filtered volume, which consists out of 181x299x305 double matrices(=FilteredVolume). The size of the voxels are [0.0500 0.0500 0.0500].
The number of slides from which the volume was created are 181.
So I was trying to write it like this:

img=cli_imageread(double.FilteredVolume);

value=cli_imageread(inputParams.name)

cli_imagewrite(double.FilteredVolume, img);

But then the error appears:

The class double has no Constant property or Static method named ‘FilteredVolume’.

Error in matrixmatlab1 (line 40)
img=cli_imageread(double.FilteredVolume);

I know that I´m missing out something, could you maybe give me a suggestion. I would be really pleased!

I just realized that I was posting just:
value=cli_imageread(inputParams.name)

instead what I wrote:
value=cli_imageread(double.‘FilteredVolume’)

Hi again,
I was trying also the way which was described in

https://github.com/PlusToolkit/PlusMatlabUtils

and load the following code:

igtlConnection = igtlConnect(’…’,18944);
transform.name = ‘NeedleToTracker’;

startTime = igtlTimestampNow();

for t=1:1000
t=igtlTimestampNow()-startTime;
transform.matrix = FilteredVolume;
transform.timestamp = igtlTimestampNow();
transform
igtlSendTransform(igtlConnection, transform);
pause(0.1)
end

igtlDisconnect(igtlConnection);

And the Transform was loaded into the Slicer, but not as I expected
the volume is displayed like this:
image
instead of showing me a structure of vessels…

I am thankful for all help and information!

MatlabOpenIGTLinkInterface in PlusMatlabUtils can only send transformation matrices, not images.

There are two approaches to transfer images from Matlab to Slicer:

  1. MatlabBridge (I would recommend this)

Slicer drives Matlab, so you must not even start Matlab, but Slicer will, when the user runs your Matlab module in Slicer. You need to create a module using Matlab module generator, customize the XML file to specify user interface for your module, and customize the m file to customize the behavior of your module.

  1. Manually transfer through files

Save volume to file from Matlab using nrrdwrite.m and load the created .nrrd file into Slicer manually or by running a command like this:

"C:\Program Files\Slicer 4.9.0-2018-06-01\Slicer.exe" --python-code "slicer.util.loadVolume(r'c:\Users\msliv\Documents\Data\MRHead.nrrd')"

Thank you for your suggestions!

I was creating a new Module in Slicer, which worked (called ‘Matrix1’)
I was saving the needed Matrix in a mat file (‘matrixfilteredvolume.mat’) and I was loadinng it into the .m file from Matrix1 module. Then I was saving the mat file (FilteredVolume matrix) under a new variable called ‘Matrix’. I was changing the XML file like this:

<?xml version="1.0" encoding="utf-8"?>
<executable>
  <category>Matlab</category>
  <title>Matrix1</title>
  <description><![CDATA[Perform a simple image processing and image statistics computation in Matlab.]]></description>
  <version>0.0.0.1</version>
  <documentation-url>http://www.slicer.org/slicerWiki/index.php/Documentation/Nightly/Extensions/MatlabBridge</documentation-url>
  <license/>
  <contributor>Andras Lasso (PerkLab)</contributor>
  <acknowledgements><![CDATA[SparKit is a project funded by Cancer Care Ontarioand the Ontario Consortium for Adaptive Interventions in Radiation Oncology (OCAIRO) to provide free, open-source toolset for radiotherapy and related image-guided interventions.]]></acknowledgements>
  <parameters>
    <label>Processing Parameters</label>
    <description><![CDATA[Parameters for the processing]]></description>
    <integer>
      <label>Threshold</label>
      <description><![CDATA[All voxels below this value will be set to zero]]></description>
      <longflag>threshold</longflag>
      <default>50</default>
      <constraints>
        <minimum>-2000</minimum>
        <maximum>5000</maximum>
        <step>5</step>
      </constraints>      
    </integer>
  </parameters>
  <parameters>
    <label>IO</label>
    <description><![CDATA[Input/output parameters]]></description>
    <image>
      <label>Matrix</label>
      <description><![CDATA[Input volume to be filtered]]></description>
      <longflag>inputvolume</longflag>
      <channel>input</channel>
    </image>
    <image>
      <label>Matrix</label>
      <description><![CDATA[Output filtered]]></description>
      <longflag>outputvolume</longflag>
      <channel>output</channel>
    </image>
  </parameters>
  <parameters>
    <label>Output</label>
    <description>Matlab command output</description>
    <double>
      <label>Minimum</label>
      <description><![CDATA[Image mininum]]></description>
      <name>min</name>
      <channel>output</channel>
      <default></default>
    </double>    
    <double>
      <label>Maximum</label>
      <description><![CDATA[Image maximum]]></description>
      <name>max</name>
      <channel>output</channel>
      <default></default>
    </double>    
  </parameters>
</executable>

and where running the code of the .m file through the new Slicer module:

function outputParams=Matrix1(inputParams)

load 'matrixfilteredvolume.mat'

Matrix = FilteredVolume

img=cli_imageread(inputParams.'Matrix');

outputParams.min=min(min(min(img.pixelData)));
outputParams.max=max(max(max(img.pixelData)));

img.pixelData=(double(img.pixelData)>inputParams.threshold)*100;

cli_imagewrite(inputParams.'Matrix', img);

The following Error appeared:
Matrix1 standard error:

Failed to execute Matlab function: Matrix1, received the following error message:
ERROR: Command execution failed. Error: File: Matrix1.m Line: 16 Column: 32
Unexpected MATLAB expression.

Error in cli_commandserver (line 91)
response=evalc(cmd);

Error in run (line 91)
evalin(‘caller’, strcat(script, ‘;’));


Did I load the matrix in a wrong way?

Have a closer look at the originally generated module skeleton. You refer to parameters by inputParams.variableName (not quoted like ‘variableName’).

Do you want to pass an image or a transformation matrix between Matlab and Slicer?

If you are just learning Matlab then I would recommend to consider using Python instead. While Matlab still has somewhat better IDE and documentation than Python but in most other things are Python is much more capable, it’s free, open-source, has huge community, many libraries, etc., so overall learning it is probably a better long-term investment.

I want to pass a matrix

Do you mean a 4x4 homogeneous transformation matrix?

it consists of the following parameters: 181x299x305 double

i= 181 are the slices from which it was created and the SizeVoxel are [0.050000000000000 0.050000000000000 0.050000000000000]

So, you want to pass an image volume. Then the default generated skeleton code should work as is. First don’t modify anything in it and see if it computes thresholded image. If everything is OK then modify the script step-by-step to see what changes cause errors.

i´m kind of stuck in the modifying part

I´m not sure wheter I should use this:

%
load ‘matrixfilteredvolume.mat’

Matrix = FilteredVolume

img=cli_imageread(Matrix);

outputParams.min=min(min(min(img.pixelData)));
outputParams.max=max(max(max(img.pixelData)));

img.pixelData=(double(img.pixelData)>inputParams.threshold)*100;

cli_imagewrite(Matrix, img);

or if I have to add infos about the slices and spaces too

Right now, your code has two issues:

  1. There is no such variable as Matrix. You’ve only set the label to Matrix in the XML file, but the variable name is generated from the longflag parameter.
  2. You added single-quotes around variable name. That’s invalid syntax, just remove the quotes.

Image origin, spacing, and axis directions are stored in img.ijkToLpsTransform. This matrix is a homogeneous transform, which transforms voxel (IJK) coordinates to physical (LPS) coordinates. Top-left 3x3 submatrix is axis direction and spacing. Top-right 3-element column vector is the image origin.

yes I was going over this function already but I don´t understand this part:

% Define origin, spacing, axis directions by a homogeneous transformation matrix:
img.ijkToLpsTransform = [ 1.2 0 0 10; 0 1.2 0 12; 0 0 3.0 -22; 0 0 0 1];

I was applying the following parameters:

load 'matrixfilteredvolume.mat'

Matrix = FilteredVolume

L = max(Matrix)

[X,Y,Z]=meshgrid(1:size(L,1), 1:size(L,2), 1:size(L,3))

img.pixelData = x/3+y/4+z/2;
 
% Define origin, spacing, axis directions by a homogeneous transformation matrix:
img.ijkToLpsTransform = [ 0.05 0 0 181; 0 0.05 0 299; 0 0 0.05 305; 0 0 0 1];
 
% Enable compression

img.metaData.encoding='gzip';

nrrdwrite('testOutput.nrrd', img);

% Open file for writing
fid=fopen(outputFilename, 'w');
if(fid<=0)

There is no such variable as Matrix. You’ve only set the label to Matrix in the XML file, but the variable name is generated from the longflag parameter.

so I have to change the location of ‘Matrix’ in the XML file to here:

<label>Threshold</label>
      <description><![CDATA[All voxels below this value will be set to zero]]></description>
      <longflag>Matrix</longflag>

You added single-quotes around variable name. That’s invalid syntax, just remove the quotes.

where exactly do you mean?

sorry this part was lost in the reply:
so I have to change the location of ‘Matrix’ in the XML file to here:
Threshold

<label>Threshold</label>
      <description><![CDATA[All voxels below this value will be set to zero]]></description>
      <longflag>Matrix</longflag>