Tips for VVectors and Structures in ESAPI

João Castelo
6 min readSep 4, 2020

Hey guys!

Today I will write about some shortcuts and interesting things I’ve learned when playing with structures and VVectors in ESAPI.

All the code will be shared at my GitHub page! So don’t worry about copy and paste today!

What are VVectors?

The VVector class is a recipient to the coordinates in the X, Y and Z direction in the CT DICOM. You can perform the usual vector operations, such as dot product, vector product, scalar product, addition, subtraction.

How do I access the coordinates:

You can access via vVector.x, vVector.y and vVector.z

It’s interesting that you can also treat it like a C# array, vVector[0] (access x coordinates) and so on.

Where is the origin?

For VVectors, the origin is placed in the lower left corner of a Head First Supine Image and it is called Dicom Origin, it grows in millimeters from feet to head, top to bottom and left to right. When you want to locate a point in Script, remember that the UI coordinates are based on the User Origin (variable), but the ESAPI coordinates are based on the DICOM Origin (fixed).

the DICOM coordinates of the center point of the upper-left hand corner voxel of the first image plane.

In which Slice this VVector is?

It depends on the image and Structure set you are accessing:

public static int _GetSlice(double z, StructureSet SS)       {                       var imageRes = SS.Image.ZRes;
return Convert.ToInt32((z - SS.Image.Origin.z) / imageRes);
}

How can I get the user’s coordinates:

Imagine you’re automating a plan creation, you should return information to the user in the coordinate system he’s used to, ESAPI class Image has a dedicated method to do this:

Image.DicomToUser(vVector, plan) //Outputs the User Coordinates

How to access a Structure:

Via context, using Id to find the structure:

var ptv = scriptContext.StructureSet.Structures.First(e => e.Id == "PTV");

In which slice is the center point of a structure?

You can access the Center Point via the Strucuture Properties:

ptv.CenterPoint; // VVector

Where does it ends and begins, introducing MeshGeometry:

Every structure segment is bounded within a box accessible via structure properties called MeshGeometry.

MeshGeometry representation

You can get the list of slices of the structure is using the following method:

public static IEnumerable<int> _GetMeshBounds(Structure structure, StructureSet SS)       { 
var mesh = structure.MeshGeometry.Bounds;
var meshLow = _GetSlice(mesh.Z, SS);
var meshUp = _GetSlice(mesh.Z + mesh.SizeZ, SS) + 1; return Enumerable.Range(meshLow, meshUp);
}

WPF ViewPort3D:

You can use the MeshGeometry to plot the structures via WPF.

We’ll use it to perform some checks later in this article.

You can check the code in my GitHub page. This binary plugin called StructureOnWPF can copy and move structures based on the selected structure.

Remember to run this code on TBOX… It’s just a test, and it’s not recommended for clinical use.

StructureOnWPF Project, Left Slider (Zoom), Right Sliders (Rotation), The text box, fields for displacements in mm

A structure contour is a List of VVectors:

If you want to get a structure contour, be prepared to play with the infamous VVector[][].

ptv.GetContoursOnImagePlane(plane);

It is an array of arrays of VVectors 😥.

You can access the x,y and z positions of the points via MeshGeometry.

It is simpler than the former alternative, and you can transform it into VVectors.

contours = ptv.MeshGeometry.Points.Select(e=>new VVector(e.x,e.y,e.z));

How to draw a structure?

To be able to create your own structures, you have to work with this method:

ptv.AddContourOnImagePlane(arrayOfVvector, slice);

This is interesting… Even knowing that your VVectors have the Z coordinates, ESAPI needs you to explicitly tell which slice to draw, and it will use only the X and Y coordinates.

Be careful with high resolution:

From what I’ve experienced, a high resolution structure has 3 times more points than a default accuracy structure. One interesting thing is that when using the AddContourOnImagePlane, if you add all the points that exists in a high resolution structure, it will convert to default and you’ll miss the contour.

Drawing the structure, but not converting to high resolution before.

DicomTypes:

Possible values are “AVOIDANCE”, “CAVITY”, “CONTRAST_AGENT”, “CTV”, “EXTERNAL”, “GTV”, “IRRAD_VOLUME”, “ORGAN”, “PTV”, “TREATED_VOLUME”, “SUPPORT”, “FIXATION”, “CONTROL”, and “DOSE_REGION”.

How to operate between structures:

This can easily be performed using the SegmentVolume property:

var ss =  scriptContext.StructureSet;
var overlap = ss.
AddStructure("CONTROL",CheckStructureIds(ss,"organ_ovl");
overlap.SegmentVolume = ptv.SegmentVolume.And(oar.SegmentVolume);

Check for high resolution, you can’t operate in ESAPI between default and high resolution segment volumes.

Contouring vs ESAPI:

If you’ve ever wondered if ESAPI and contouring operations yields the same results, unfortunately they do not.

Since operations in ESAPI are done with a different engine, they lead to a different result, however this has little optimization impact (this could be a nice science experiment). Here is an overlap example:

Script Overlap is in blue, it’s statistics are represented in the left. The magenta is the contouring Overlap, it’s statistics are represented in the right.

Be careful with Structure Id max length:

Sometimes we’re so focused in getting the code to work that we forget the maximum chars for structures in ESAPI. It is always possible to limit and escape this kind of exception with simple functions:

Structure Color:

You simply can’t change the structure color via ESAPI.

Maximum number of Structures:

You can have 99 structures at maximum inside your StructureSet, I’ve never knew about this number, but I got few errors in my last project due to this limitation.

Moving a structure:

In the code below, I share a helper class. I use this code whenever I need to move a structure.

It uses all the previous concepts we’ve been discussing in this article.

Real number coordinates versus CT resolution:

You can assign any real number to a VVector. This can lead to errors during the code execution, here I’m trying to add a contour point at position (1000,1000,1000). Although I’m not prohibited to create this VVector I can’t add this contour to the Structure, as shown below:

If you try to displace below the CT resolution:

In this example the CT X and Y resolutions are 0.7 mm, then we displace 0.4 mm in those axes; The Z resolution is 1 mm, then we displace 0.8 mm in that axis;

For my own surprise, the structure is indeed displaced, but the results are not linear, some points do arrange and others don’t.

Here is the result if you displace with the CT resolution:

The result makes more sense.

More on operation between structures:

Here I’ll leave a link to Max’s SimpleGUI_ContourUnions repo on GitHub.

It is a simple user interface of PTV and GTV unions that can get you insight in how to develop your own optimization structures routine.

Photo by Leon Seibert on Unsplash

Thanks for reading!

See you next time!

What do you guys think it is a good topic for my next post?

Are you used to use GitHub?

Thanks to Jonas to review this article!

--

--

João Castelo

Radiation Therapy Medical Physicist and Programmer